Blame SOURCES/sysstat-10.1.5-tapestat.patch

ab3721
diff -uprN sysstat-10.1.5.orig/Makefile.in sysstat-10.1.5/Makefile.in
ab3721
--- sysstat-10.1.5.orig/Makefile.in	2016-06-01 12:46:28.193462305 +0200
ab3721
+++ sysstat-10.1.5/Makefile.in	2016-06-01 12:46:52.027338556 +0200
ab3721
@@ -160,7 +160,7 @@ NLSPOT= $(NLSPO:.po=.pot)
ab3721
 % : %.o
ab3721
 	$(CC) -o $@ $(CFLAGS) $^ $(LFLAGS)
ab3721
 
ab3721
-all: sadc sar sadf iostat mpstat pidstat nfsiostat-sysstat cifsiostat locales
ab3721
+all: sadc sar sadf iostat tapestat mpstat pidstat nfsiostat-sysstat cifsiostat locales
ab3721
 
ab3721
 common.o: common.c version.h common.h ioconf.h sysconfig.h
ab3721
 
ab3721
@@ -221,6 +221,10 @@ iostat.o: iostat.c iostat.h version.h co
ab3721
 
ab3721
 iostat: iostat.o librdstats.a libsyscom.a
ab3721
 
ab3721
+tapestat.o: tapestat.c tapestat.h version.h common.h
ab3721
+
ab3721
+tapestat: tapestat.o librdstats.a libsyscom.a
ab3721
+
ab3721
 pidstat.o: pidstat.c pidstat.h version.h common.h rd_stats.h
ab3721
 
ab3721
 pidstat: pidstat.o librdstats.a libsyscom.a
ab3721
@@ -269,6 +273,8 @@ ifeq ($(INSTALL_DOC),y)
ab3721
 	$(INSTALL_DATA) $(MANGRPARG) man/sysstat.5 $(DESTDIR)$(MAN5_DIR)
ab3721
 	rm -f $(DESTDIR)$(MAN1_DIR)/iostat.1*
ab3721
 	$(INSTALL_DATA) $(MANGRPARG) man/iostat.1 $(DESTDIR)$(MAN1_DIR)
ab3721
+	rm -f $(DESTDIR)$(MAN1_DIR)/tapestat.1*
ab3721
+	$(INSTALL_DATA) $(MANGRPARG) man/tapestat.1 $(DESTDIR)$(MAN1_DIR)
ab3721
 	rm -f $(DESTDIR)$(MAN1_DIR)/mpstat.1*
ab3721
 	$(INSTALL_DATA) $(MANGRPARG) man/mpstat.1 $(DESTDIR)$(MAN1_DIR)
ab3721
 	rm -f $(DESTDIR)$(MAN1_DIR)/pidstat.1*
ab3721
@@ -288,6 +294,7 @@ ifeq ($(COMPRESS_MANPG),y)
ab3721
 	$(ZIP) $(DESTDIR)$(MAN1_DIR)/sadf.1
ab3721
 	$(ZIP) $(DESTDIR)$(MAN5_DIR)/sysstat.5
ab3721
 	$(ZIP) $(DESTDIR)$(MAN1_DIR)/iostat.1
ab3721
+	$(ZIP) $(DESTDIR)$(MAN1_DIR)/tapestat.1
ab3721
 	$(ZIP) $(DESTDIR)$(MAN1_DIR)/mpstat.1
ab3721
 	$(ZIP) $(DESTDIR)$(MAN1_DIR)/pidstat.1
ab3721
 	$(ZIP) $(DESTDIR)$(MAN1_DIR)/nfsiostat-sysstat.1
ab3721
@@ -328,6 +335,7 @@ endif
ab3721
 	$(INSTALL_BIN) sar $(DESTDIR)$(BIN_DIR)
ab3721
 	$(INSTALL_BIN) sadf $(DESTDIR)$(BIN_DIR)
ab3721
 	$(INSTALL_BIN) iostat $(DESTDIR)$(BIN_DIR)
ab3721
+	$(INSTALL_BIN) tapestat $(DESTDIR)$(BIN_DIR)
ab3721
 	$(INSTALL_BIN) mpstat $(DESTDIR)$(BIN_DIR)
ab3721
 	$(INSTALL_BIN) pidstat $(DESTDIR)$(BIN_DIR)
ab3721
 	$(INSTALL_BIN) nfsiostat-sysstat $(DESTDIR)$(BIN_DIR)
ab3721
@@ -395,6 +403,7 @@ ifeq ($(INSTALL_DOC),y)
ab3721
 	rm -f $(DESTDIR)$(MAN1_DIR)/sadf.1*
ab3721
 	rm -f $(DESTDIR)$(MAN5_DIR)/sysstat.5*
ab3721
 	rm -f $(DESTDIR)$(MAN1_DIR)/iostat.1*
ab3721
+	rm -f $(DESTDIR)$(MAN1_DIR)/tapestat.1*
ab3721
 	rm -f $(DESTDIR)$(MAN1_DIR)/mpstat.1*
ab3721
 	rm -f $(DESTDIR)$(MAN1_DIR)/pidstat.1*
ab3721
 	rm -f $(DESTDIR)$(MAN1_DIR)/nfsiostat-sysstat.1*
ab3721
@@ -423,6 +432,7 @@ uninstall_base: uninstall_man uninstall_
ab3721
 	rm -f $(DESTDIR)$(BIN_DIR)/sar
ab3721
 	rm -f $(DESTDIR)$(BIN_DIR)/sadf
ab3721
 	rm -f $(DESTDIR)$(BIN_DIR)/iostat
ab3721
+	rm -f $(DESTDIR)$(BIN_DIR)/tapestat
ab3721
 	rm -f $(DESTDIR)$(BIN_DIR)/mpstat
ab3721
 	rm -f $(DESTDIR)$(BIN_DIR)/pidstat
ab3721
 	rm -f $(DESTDIR)$(BIN_DIR)/nfsiostat-sysstat
ab3721
@@ -487,7 +497,7 @@ po-files:
ab3721
 endif
ab3721
 
ab3721
 clean:
ab3721
-	rm -f sadc sar sadf iostat mpstat pidstat nfsiostat-sysstat cifsiostat *.o *.a core TAGS
ab3721
+	rm -f sadc sar sadf iostat tapestat mpstat pidstat nfsiostat-sysstat cifsiostat *.o *.a core TAGS
ab3721
 	find nls -name "*.gmo" -exec rm -f {} \;
ab3721
 
ab3721
 almost-distclean: clean nls/sysstat.pot
ab3721
diff -uprN sysstat-10.1.5.orig/man/tapestat.1 sysstat-10.1.5/man/tapestat.1
ab3721
--- sysstat-10.1.5.orig/man/tapestat.1	1970-01-01 01:00:00.000000000 +0100
ab3721
+++ sysstat-10.1.5/man/tapestat.1	2016-06-01 12:46:52.028338551 +0200
ab3721
@@ -0,0 +1,216 @@
ab3721
+.TH TAPESTAT 1 "MARCH 2016" Linux "Linux User's Manual" -*- nroff -*-
ab3721
+.SH NAME
ab3721
+tapestat \- Report tape statistics.
ab3721
+.SH SYNOPSIS
ab3721
+.B tapestat [ -k | -m ] [ -t ] [ -V ] [ -y ] [ -z ] [
ab3721
+.I interval
ab3721
+.B [
ab3721
+.I count
ab3721
+.B ] ]
ab3721
+.SH DESCRIPTION
ab3721
+The
ab3721
+.B tapestat
ab3721
+command is used for monitoring the activity of tape drives connected to a system.
ab3721
+
ab3721
+The first report generated by the
ab3721
+.B tapestat
ab3721
+command provides statistics
ab3721
+concerning the time since the system was booted, unless the
ab3721
+.B -y
ab3721
+option is used, when this first report is omitted.
ab3721
+Each subsequent report
ab3721
+covers the time since the previous report.
ab3721
+
ab3721
+The
ab3721
+.I interval
ab3721
+parameter specifies the amount of time in seconds between
ab3721
+each report.
ab3721
+The
ab3721
+.I count
ab3721
+parameter can be specified in conjunction with the
ab3721
+.I interval
ab3721
+parameter. If the
ab3721
+.I count
ab3721
+parameter is specified, the value of
ab3721
+.I count
ab3721
+determines the number of reports generated at
ab3721
+.I interval
ab3721
+seconds apart. If the
ab3721
+.I interval
ab3721
+parameter is specified without the
ab3721
+.I count
ab3721
+parameter, the
ab3721
+.B tapestat
ab3721
+command generates reports continuously.
ab3721
+
ab3721
+.SH REPORT
ab3721
+The
ab3721
+.B tapestat
ab3721
+report provides statistics for each tape drive connected to the system.
ab3721
+The following data are displayed:
ab3721
+
ab3721
+.B r/s 
ab3721
+.RS
ab3721
+The number of reads issued expressed as the number per second averaged over the interval.
ab3721
+
ab3721
+.RE
ab3721
+.B w/s
ab3721
+.RS
ab3721
+The number of writes issued expressed as the number per second averaged over the interval.
ab3721
+
ab3721
+.RE
ab3721
+.B kB_read/s | MB_read/s
ab3721
+.RS
ab3721
+The amount of data read expressed in kilobytes (by default or if option -k used) or
ab3721
+megabytes (if option -m used) per second averaged over the interval.
ab3721
+
ab3721
+.RE
ab3721
+.B kB_wrtn/s | MB_wrtn/s
ab3721
+.RS
ab3721
+The amount of data written expressed in kilobytes (by default or if option -k used) or
ab3721
+megabytes (if option -m used) per second averaged over the interval.
ab3721
+
ab3721
+.RE
ab3721
+.B %Rd
ab3721
+.RS
ab3721
+Read percentage wait - The percentage of time over the interval spent waiting for read requests
ab3721
+to complete.
ab3721
+The time is measured from when the request is dispatched to the SCSI mid-layer until it signals
ab3721
+that it completed.
ab3721
+
ab3721
+.RE
ab3721
+.B %Wr
ab3721
+.RS
ab3721
+Write percentage wait - The percentage of time over the interval spent waiting for write requests
ab3721
+to complete. The time is measured from when the request is dispatched to the SCSI mid-layer until
ab3721
+it signals that it completed.
ab3721
+
ab3721
+.RE
ab3721
+.B %Oa
ab3721
+.RS
ab3721
+Overall percentage wait - The percentage of time over the interval spent waiting for any
ab3721
+I/O request to complete (read, write, and other).
ab3721
+
ab3721
+.RE
ab3721
+.B Rs/s
ab3721
+.RS
ab3721
+The number of I/Os, expressed as the number per second averaged over the interval, where
ab3721
+a non-zero residual value was encountered.
ab3721
+
ab3721
+.RE
ab3721
+.B Ot/s
ab3721
+.RS
ab3721
+The number of I/Os, expressed as the number per second averaged over the interval, that
ab3721
+were included as "other". Other I/O includes ioctl calls made to the tape driver and
ab3721
+implicit operations performed by the tape driver such as rewind on close
ab3721
+(for tape devices that implement rewind on close). It does not include any I/O performed
ab3721
+using methods outside of the tape driver (e.g. via sg ioctls).
ab3721
+.RE
ab3721
+.RE
ab3721
+.SH OPTIONS
ab3721
+.IP -k
ab3721
+Show the amount of data written or read in kilobytes per second instead of megabytes.
ab3721
+This option is mutually exclusive with -m.
ab3721
+.IP -m
ab3721
+Show the amount of data written or read in megabytes per second instead of kilobytes.
ab3721
+This option is mutually exclusive with -k.
ab3721
+.IP -t
ab3721
+Display time stamps. The time stamp format may depend
ab3721
+on the value of the S_TIME_FORMAT environment variable (see below).
ab3721
+.IP -V
ab3721
+Print version and exit.
ab3721
+.IP -y
ab3721
+Omit the initial statistic showing values since boot.
ab3721
+.IP -z
ab3721
+Tell
ab3721
+.B tapestat
ab3721
+to omit output for any tapes for which there was no activity
ab3721
+during the sample period.
ab3721
+
ab3721
+.SH CONSIDERATIONS
ab3721
+It is possible for a percentage value (read, write, or other) to be greater than 100 percent
ab3721
+(the
ab3721
+.B tapestat
ab3721
+command will never show a percentage value more than 999).
ab3721
+If rewinding a tape takes 40 seconds where the interval time is 5 seconds the %Oa value
ab3721
+would show as 0 in the intervals before the rewind completed and then show as approximately
ab3721
+800 percent when the rewind completes. 
ab3721
+
ab3721
+Similar values will be observed for %Rd and %Wr if a tape drive stops reading or writing
ab3721
+and then restarts (that is it stopped streaming). In such a case you may see the r/s or w/s drop to zero and the %Rd/%Wr value could be higher than 100 when reading or writing continues
ab3721
+(depending on how long it takes to restart writing or reading).
ab3721
+This is only an issue if it happens a lot as it may cause tape wear and will impact
ab3721
+on the backup times.
ab3721
+
ab3721
+For fast tape drives you may see low percentage wait times.
ab3721
+This does not indicate an issue with the tape drive. For a slower tape drive (e.g. an older
ab3721
+generation DDS drive) the speed of the tape (and tape drive) is much slower than filesystem I/O,
ab3721
+percent wait times are likely to be higher. For faster tape drives (e.g. LTO) the percentage
ab3721
+wait times are likely to be lower as program writing to or reading from tape is going
ab3721
+to be doing a lot more filesystem I/O because of the higher throughput.
ab3721
+
ab3721
+Although tape statistics are implemented in the kernel using atomic variables they cannot be
ab3721
+read atomically as a group. All of the statistics values are read from different files under
ab3721
+/sys, because of this there may be I/O completions while reading the different files for the
ab3721
+one tape drive. This may result in a set of statistics for a device that contain some values
ab3721
+before an I/O completed and some after.
ab3721
+
ab3721
+This command uses rounding down as the rounding method when calculating per second statistics.
ab3721
+If, for example, you are using dd to copy one tape to another and running
ab3721
+.B tapestat
ab3721
+with an interval of 5 seconds and over the interval there were 3210 writes and 3209 reads
ab3721
+then w/s would show 642 and r/s 641 (641.8 rounded down to 641). In such a case if it was
ab3721
+a tar archive being copied (with a 10k block size) you would also see a difference between
ab3721
+the kB_read/s and kB_wrtn/s of 2 (one I/O 10k in size divided by the interval period of 5
ab3721
+seconds). If instead there were 3210 writes and 3211 reads both w/s and r/s would both show
ab3721
+642 but you would still see a difference between the kB_read/s and kB_wrtn/s values of 2 kB/s.
ab3721
+
ab3721
+This command is provided with an interval in seconds. However internally the interval is
ab3721
+tracked per device and can potentially have an effect on the per second statistics reported.
ab3721
+The time each set of statistics is captured is kept with those statistics. The difference
ab3721
+between the current and previous time is converted to milliseconds for use in calculations.
ab3721
+We can look at how this can impact the statistics reported if we use an example of a tar
ab3721
+archive being copied between two tape drives using dd. If both devices reported 28900 kilobytes
ab3721
+transferred and the reading tape drive had an interval of 5001 milliseconds and the writing
ab3721
+tape drive 5000 milliseconds that would calculate out as 5778 kB_read/s and 5780 kB_wrtn/s.
ab3721
+
ab3721
+The impact of some retrieving statistics during an I/O completion, rounding down, and small differences in the interval period on the statistics calculated should be minimal but may be non-zero.
ab3721
+.SH ENVIRONMENT
ab3721
+The
ab3721
+.B tapestat
ab3721
+command takes into account the following environment variable:
ab3721
+
ab3721
+.IP S_TIME_FORMAT
ab3721
+If this variable exists and its value is
ab3721
+.BR ISO
ab3721
+then the current locale will be ignored when printing the date in the report
ab3721
+header. The
ab3721
+.B tapestat
ab3721
+command will use the ISO 8601 format (YYYY-MM-DD) instead.
ab3721
+The timestamp displayed with option -t will also be compliant with ISO 8601
ab3721
+format.
ab3721
+
ab3721
+.SH BUGS
ab3721
+.I /sys
ab3721
+filesystem must be mounted for
ab3721
+.B tapestat
ab3721
+to work. It will not work on kernels that do not have sysfs support
ab3721
+
ab3721
+This command requires kernel version 4.2 or later
ab3721
+(or tape statistics support backported for an earlier kernel version).
ab3721
+
ab3721
+.SH FILES
ab3721
+.I /sys/class/scsi_tape/st<num>/stats/*
ab3721
+Statistics files for tape devices.
ab3721
+
ab3721
+.I /proc/uptime
ab3721
+contains system uptime.
ab3721
+.SH AUTHOR
ab3721
+Initial revision by Shane M. SEYMOUR (shane.seymour <at> hpe.com)
ab3721
+.br
ab3721
+Modified for sysstat by Sebastien Godard (sysstat <at> orange.fr)
ab3721
+.SH SEE ALSO
ab3721
+.BR iostat (1),
ab3721
+.BR mpstat (1)
ab3721
+
ab3721
+.I http://pagesperso-orange.fr/sebastien.godard/
ab3721
diff -uprN sysstat-10.1.5.orig/tapestat.c sysstat-10.1.5/tapestat.c
ab3721
--- sysstat-10.1.5.orig/tapestat.c	1970-01-01 01:00:00.000000000 +0100
ab3721
+++ sysstat-10.1.5/tapestat.c	2016-06-01 12:54:20.384015957 +0200
ab3721
@@ -0,0 +1,697 @@
ab3721
+/*
ab3721
+ * tapestat: report tape statistics
ab3721
+ * (C) 2015 Hewlett-Packard Development Company, L.P.
ab3721
+ *
ab3721
+ * Initial revision by Shane M. SEYMOUR (shane.seymour <at> hpe.com)
ab3721
+ * Modified for sysstat by Sebastien GODARD (sysstat <at> orange.fr)
ab3721
+ *
ab3721
+ ***************************************************************************
ab3721
+ * This program is free software; you can redistribute it and/or modify it *
ab3721
+ * under the terms of the GNU General Public License as published  by  the *
ab3721
+ * Free Software Foundation; either version 2 of the License, or (at  your *
ab3721
+ * option) any later version.                                              *
ab3721
+ *                                                                         *
ab3721
+ * This program is distributed in the hope that it  will  be  useful,  but *
ab3721
+ * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
ab3721
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
ab3721
+ * for more details.                                                       *
ab3721
+ *                                                                         *
ab3721
+ * You should have received a copy of the GNU General Public License along *
ab3721
+ * with this program; if not, write to the Free Software Foundation, Inc., *
ab3721
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+
ab3721
+#include <stdio.h>
ab3721
+#include <string.h>
ab3721
+#include <stdlib.h>
ab3721
+#include <unistd.h>
ab3721
+#include <time.h>
ab3721
+#include <dirent.h>
ab3721
+#define __DO_NOT_DEFINE_COMPILE
ab3721
+#include <regex.h>
ab3721
+#include <inttypes.h>
ab3721
+#include <stdint.h>
ab3721
+#include <signal.h>
ab3721
+#include <sys/stat.h>
ab3721
+#include <sys/time.h>
ab3721
+#include <sys/utsname.h>
ab3721
+#include <sys/param.h>
ab3721
+#undef HZ /* sys/param.h defines HZ but needed for MAXPATHLEN */
ab3721
+
ab3721
+#include "version.h"
ab3721
+#include "tapestat.h"
ab3721
+#include "common.h"
ab3721
+
ab3721
+#ifdef USE_NLS
ab3721
+#include <locale.h>
ab3721
+#include <libintl.h>
ab3721
+#define _(string) gettext(string)
ab3721
+#else
ab3721
+#define _(string) (string)
ab3721
+#endif
ab3721
+
ab3721
+#define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
ab3721
+char *sccsid(void) { return (SCCSID); }
ab3721
+
ab3721
+int cpu_nr = 0;		/* Nb of processors on the machine */
ab3721
+int flags = 0;		/* Flag for common options and system state */
ab3721
+
ab3721
+long interval = 0;
ab3721
+char timestamp[64];
ab3721
+
ab3721
+struct sigaction alrm_act;
ab3721
+
ab3721
+/*
ab3721
+ * For tape stats - it would be extremely rare for there to be a very large
ab3721
+ * number of tape drives attached to a system. I wouldn't expect to see more
ab3721
+ * than 20-30 in a very large configuration and discontinguous ones should
ab3721
+ * be even more rare. Because of this we keep the old and new data in a
ab3721
+ * simple data structure with the tape index being the number after the tape
ab3721
+ * drive, st0 at index 0, etc.
ab3721
+ */
ab3721
+int max_tape_drives = 0;
ab3721
+struct tape_stats *tape_new_stats = { NULL };
ab3721
+struct tape_stats *tape_old_stats = { NULL };
ab3721
+regex_t tape_reg;
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Print usage and exit.
ab3721
+ *
ab3721
+ * IN:
ab3721
+ * @progname	Name of sysstat command.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void usage(char *progname)
ab3721
+{
ab3721
+	fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
ab3721
+		progname);
ab3721
+	fprintf(stderr, _("Options are:\n"
ab3721
+			  "[ -k | -m ] [ -t ] [ -V ] [ -y ] [ -z ]\n"));
ab3721
+	exit(1);
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * SIGALRM signal handler. No need to reset the handler here.
ab3721
+ *
ab3721
+ * IN:
ab3721
+ * @sig	Signal number.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void alarm_handler(int sig)
ab3721
+{
ab3721
+	alarm(interval);
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Initialization.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void tape_initialise(void)
ab3721
+{
ab3721
+	/* How many processors on this machine? */
ab3721
+	cpu_nr = get_cpu_nr(~0);
ab3721
+
ab3721
+	/* Compile regular expression for tape names */
ab3721
+        if (regcomp(&tape_reg, "^st[0-9]+$", REG_EXTENDED) != 0) {
ab3721
+		exit(1);
ab3721
+        }
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Free structures.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void tape_uninitialise(void)
ab3721
+{
ab3721
+	regfree(&tape_reg);
ab3721
+	if (tape_old_stats != NULL) {
ab3721
+		free(tape_old_stats);
ab3721
+	}
ab3721
+	if (tape_new_stats != NULL) {
ab3721
+		free(tape_new_stats);
ab3721
+	}
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Get maximum number of tapes in the system.
ab3721
+ *
ab3721
+ * RETURNS:
ab3721
+ * Number of tapes found.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+int get_max_tape_drives(void)
ab3721
+{
ab3721
+	DIR *dir;
ab3721
+	struct dirent *entry;
ab3721
+	int new_max_tape_drives, tmp, num_stats_dir = 0;
ab3721
+	regmatch_t match;
ab3721
+	char stats_dir[MAXPATHLEN + 1];
ab3721
+	struct stat stat_buf;
ab3721
+
ab3721
+	new_max_tape_drives = max_tape_drives;
ab3721
+
ab3721
+	/* Open sysfs tree */
ab3721
+	dir = opendir(SYSFS_CLASS_TAPE_DIR);
ab3721
+	if (dir == NULL)
ab3721
+		return 0;
ab3721
+
ab3721
+	while ((entry = readdir(dir)) != NULL) {
ab3721
+		if (regexec(&tape_reg, &entry->d_name[0], 1, &match, 0) == 0) {
ab3721
+			/* d_name[2] to skip the st at the front */
ab3721
+			tmp = atoi(&entry->d_name[2]) + 1;
ab3721
+			if (tmp > new_max_tape_drives) {
ab3721
+				new_max_tape_drives = tmp;
ab3721
+			}
ab3721
+		}
ab3721
+		snprintf(stats_dir, MAXPATHLEN, "%s/%s/%s",
ab3721
+			SYSFS_CLASS_TAPE_DIR, &entry->d_name[0], "stats");
ab3721
+		if (stat(stats_dir, &stat_buf) == 0) {
ab3721
+			if (S_ISDIR(stat_buf.st_mode)) {
ab3721
+				num_stats_dir++;
ab3721
+			}
ab3721
+		}
ab3721
+	}
ab3721
+	closedir(dir);
ab3721
+
ab3721
+	/* If there are no stats directories make the new number of tape drives 0 */
ab3721
+	if (num_stats_dir == 0) {
ab3721
+		new_max_tape_drives = 0;
ab3721
+	}
ab3721
+
ab3721
+	return new_max_tape_drives;
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Check if new tapes have been added and reallocate structures accordingly.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void tape_check_tapes_and_realloc(void)
ab3721
+{
ab3721
+	int new_max_tape_drives, i;
ab3721
+
ab3721
+	/* Count again number of tapes */
ab3721
+	new_max_tape_drives = get_max_tape_drives();
ab3721
+
ab3721
+	if (new_max_tape_drives > max_tape_drives && new_max_tape_drives > 0) {
ab3721
+		/* New tapes found: Realloc structures */
ab3721
+		struct tape_stats *tape_old_stats_t = (struct tape_stats *)
ab3721
+			realloc(tape_old_stats,	sizeof(struct tape_stats) * new_max_tape_drives);
ab3721
+		struct tape_stats *tape_new_stats_t = (struct tape_stats *)
ab3721
+			realloc(tape_new_stats,	sizeof(struct tape_stats) * new_max_tape_drives);
ab3721
+		if ((tape_old_stats_t == NULL) || (tape_new_stats_t == NULL)) {
ab3721
+			if (tape_old_stats_t != NULL) {
ab3721
+				free(tape_old_stats_t);
ab3721
+				tape_old_stats_t = NULL;
ab3721
+			} else {
ab3721
+				free(tape_old_stats);
ab3721
+				tape_old_stats = NULL;
ab3721
+			}
ab3721
+			if (tape_new_stats_t != NULL) {
ab3721
+				free(tape_new_stats_t);
ab3721
+				tape_new_stats_t = NULL;
ab3721
+			} else {
ab3721
+				free(tape_new_stats);
ab3721
+				tape_new_stats = NULL;
ab3721
+			}
ab3721
+
ab3721
+			perror("realloc");
ab3721
+			exit(4);
ab3721
+		}
ab3721
+
ab3721
+		tape_old_stats = tape_old_stats_t;
ab3721
+		tape_new_stats = tape_new_stats_t;
ab3721
+
ab3721
+		for (i = max_tape_drives; i < new_max_tape_drives; i++) {
ab3721
+			tape_old_stats[i].valid = TAPE_STATS_INVALID;
ab3721
+			tape_new_stats[i].valid = TAPE_STATS_INVALID;
ab3721
+		}
ab3721
+		max_tape_drives = new_max_tape_drives;
ab3721
+	}
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Collect initial statistics for all existing tapes in the system.
ab3721
+ * This function should be called only once.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void tape_gather_initial_stats(void)
ab3721
+{
ab3721
+	int new_max_tape_drives, i;
ab3721
+	FILE *fp;
ab3721
+	char filename[MAXPATHLEN + 1];
ab3721
+
ab3721
+	/* Get number of tapes in the system */
ab3721
+	new_max_tape_drives = get_max_tape_drives();
ab3721
+
ab3721
+	if (new_max_tape_drives == 0) {
ab3721
+		/* No tapes found */
ab3721
+		fprintf(stderr, _("No tape drives with statistics found\n"));
ab3721
+		exit(1);
ab3721
+	}
ab3721
+	else {
ab3721
+		/* Allocate structures */
ab3721
+		if (tape_old_stats == NULL) {
ab3721
+			tape_old_stats = (struct tape_stats *)
ab3721
+				malloc(sizeof(struct tape_stats) * new_max_tape_drives);
ab3721
+			tape_new_stats = (struct tape_stats *)
ab3721
+				malloc(sizeof(struct tape_stats) * new_max_tape_drives);
ab3721
+			for (i = 0; i < new_max_tape_drives; i++) {
ab3721
+				tape_old_stats[i].valid = TAPE_STATS_INVALID;
ab3721
+				tape_new_stats[i].valid = TAPE_STATS_INVALID;
ab3721
+			}
ab3721
+			max_tape_drives = new_max_tape_drives;
ab3721
+		} else
ab3721
+			/* This should only be called once */
ab3721
+			return;
ab3721
+	}
ab3721
+
ab3721
+	/* Read stats for each tape */
ab3721
+	for (i = 0; i < max_tape_drives; i++) {
ab3721
+		/*
ab3721
+		 * Everything starts out valid but failing to open
ab3721
+		 * a file gets the tape drive marked invalid.
ab3721
+		 */
ab3721
+		tape_new_stats[i].valid = TAPE_STATS_VALID;
ab3721
+		tape_old_stats[i].valid = TAPE_STATS_VALID;
ab3721
+
ab3721
+		gettimeofday(&tape_old_stats[i].tv, NULL);
ab3721
+
ab3721
+		tape_new_stats[i].tv.tv_sec = tape_old_stats[i].tv.tv_sec;
ab3721
+		tape_new_stats[i].tv.tv_usec = tape_old_stats[i].tv.tv_usec;
ab3721
+
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_ns", read_time)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_ns", write_time)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "io_ns", other_time)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_byte_cnt", read_bytes)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_byte_cnt", write_bytes)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_cnt", read_count)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_cnt", write_count)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "other_cnt", other_count)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "resid_cnt", resid_count)
ab3721
+
ab3721
+		tape_old_stats[i].read_time = 0;
ab3721
+		tape_old_stats[i].write_time = 0;
ab3721
+		tape_old_stats[i].other_time = 0;
ab3721
+		tape_old_stats[i].read_bytes = 0;
ab3721
+		tape_old_stats[i].write_bytes = 0;
ab3721
+		tape_old_stats[i].read_count = 0;
ab3721
+		tape_old_stats[i].write_count = 0;
ab3721
+		tape_old_stats[i].other_count = 0;
ab3721
+		tape_old_stats[i].resid_count = 0;
ab3721
+	}
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Collect a new sample of statistics for all existing tapes in the system.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void tape_get_updated_stats(void)
ab3721
+{
ab3721
+	int i;
ab3721
+	FILE *fp;
ab3721
+	char filename[MAXPATHLEN + 1] = { 0 };
ab3721
+
ab3721
+	/* Check tapes and realloc structures if  needed */
ab3721
+	tape_check_tapes_and_realloc();
ab3721
+
ab3721
+	for (i = 0; i < max_tape_drives; i++) {
ab3721
+		/*
ab3721
+		 * Everything starts out valid but failing
ab3721
+		 * to open a file gets the tape drive marked invalid.
ab3721
+		 */
ab3721
+		tape_new_stats[i].valid = TAPE_STATS_VALID;
ab3721
+		gettimeofday(&tape_new_stats[i].tv, NULL);
ab3721
+
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_ns", read_time)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_ns", write_time)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "io_ns", other_time)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_byte_cnt", read_bytes)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_byte_cnt", write_bytes)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_cnt", read_count)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_cnt", write_count)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "other_cnt", other_count)
ab3721
+		TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "resid_cnt", resid_count)
ab3721
+
ab3721
+		if ((tape_new_stats[i].read_time < tape_old_stats[i].read_time) ||
ab3721
+		    (tape_new_stats[i].write_time < tape_old_stats[i].write_time) ||
ab3721
+		    (tape_new_stats[i].other_time < tape_old_stats[i].other_time)) {
ab3721
+			tape_new_stats[i].valid = TAPE_STATS_INVALID;
ab3721
+		}
ab3721
+	}
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Display tapes statistics headings.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void tape_write_headings(void)
ab3721
+{
ab3721
+	printf("Tape:    r/s     w/s   ");
ab3721
+	if (DISPLAY_MEGABYTES(flags)) {
ab3721
+		printf("MB_read/s   MB_wrtn/s");
ab3721
+	} else {
ab3721
+		printf("kB_read/s   kB_wrtn/s");
ab3721
+	}
ab3721
+	printf(" %%Rd %%Wr %%Oa    Rs/s    Ot/s\n");
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Calculate statistics for current tape.
ab3721
+ *
ab3721
+ * IN:
ab3721
+ * @i		Index in array for current tape.
ab3721
+ *
ab3721
+ * OUT:
ab3721
+ * @stats	Statistics for current tape.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void tape_calc_one_stats(struct calc_stats *stats, int i)
ab3721
+{
ab3721
+	uint64_t duration;
ab3721
+	double temp;
ab3721
+	FILE *fp;
ab3721
+
ab3721
+	/* Duration in ms done in ms to prevent rounding issues with using seconds */
ab3721
+	duration = (tape_new_stats[i].tv.tv_sec -
ab3721
+		tape_old_stats[i].tv.tv_sec) * 1000;
ab3721
+	duration -= tape_old_stats[i].tv.tv_usec / 1000;
ab3721
+	duration += tape_new_stats[i].tv.tv_usec / 1000;
ab3721
+
ab3721
+	/* If duration is zero we need to calculate the ms since boot time */
ab3721
+	if (duration == 0) {
ab3721
+		fp = fopen("/proc/uptime", "r");
ab3721
+
ab3721
+		/*
ab3721
+		 * Get uptime from /proc/uptime and if we can't then just set duration to
ab3721
+		 * be 0 - it will mean that we don't calculate stats.
ab3721
+		 */
ab3721
+		if (fp == NULL) {
ab3721
+			duration = 0;
ab3721
+		} else {
ab3721
+			if (fscanf(fp, "%lf", &temp) != 1) {
ab3721
+				temp = 0;
ab3721
+			}
ab3721
+			duration = (uint64_t) (temp * 1000);
ab3721
+			fclose(fp);
ab3721
+		}
ab3721
+	}
ab3721
+
ab3721
+	/* The second value passed into the macro is the thing being calculated */
ab3721
+	CALC_STAT_CNT(read_count, reads_per_second)
ab3721
+	CALC_STAT_CNT(write_count, writes_per_second)
ab3721
+	CALC_STAT_CNT(other_count, other_per_second)
ab3721
+	CALC_STAT_KB(read_bytes, kbytes_read_per_second)
ab3721
+	CALC_STAT_KB(write_bytes, kbytes_written_per_second)
ab3721
+	CALC_STAT_PCT(read_time, read_pct_wait)
ab3721
+	CALC_STAT_PCT(write_time, write_pct_wait)
ab3721
+	CALC_STAT_PCT(other_time, all_pct_wait)
ab3721
+	CALC_STAT_CNT(resid_count, resids_per_second)
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Display statistics for current tape.
ab3721
+ *
ab3721
+ * IN:
ab3721
+ * @tape	Statistics for current tape.
ab3721
+ * @i		Index in array for current tape.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void tape_write_stats(struct calc_stats *tape, int i)
ab3721
+{
ab3721
+	char buffer[32];
ab3721
+	uint64_t divisor = 1;
ab3721
+
ab3721
+	if (DISPLAY_MEGABYTES(flags))
ab3721
+		divisor = 1024;
ab3721
+
ab3721
+	sprintf(buffer, "st%i        ", i);
ab3721
+	buffer[5] = 0;
ab3721
+	printf("%s%7"PRId64" %7"PRId64" %11"PRId64
ab3721
+		" %11"PRId64" %3"PRId64" %3"PRId64" %3"PRId64
ab3721
+		" %7"PRId64" %7"PRId64"\n", buffer,
ab3721
+		tape->reads_per_second, tape->writes_per_second,
ab3721
+		tape->kbytes_read_per_second / divisor,
ab3721
+		tape->kbytes_written_per_second / divisor,
ab3721
+		tape->read_pct_wait, tape->write_pct_wait,
ab3721
+		tape->all_pct_wait, tape->resids_per_second,
ab3721
+		tape->other_per_second);
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Print everything now (stats and uptime).
ab3721
+ *
ab3721
+ * IN:
ab3721
+ * @rectime	Current date and time.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void write_stats(struct tm *rectime)
ab3721
+{
ab3721
+	int i;
ab3721
+	struct calc_stats tape;
ab3721
+	struct tape_stats *tmp;
ab3721
+
ab3721
+	/* Test stdout */
ab3721
+	TEST_STDOUT(STDOUT_FILENO);
ab3721
+
ab3721
+	/* Print time stamp */
ab3721
+	if (DISPLAY_TIMESTAMP(flags)) {
ab3721
+		if (DISPLAY_ISO(flags)) {
ab3721
+			strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
ab3721
+		}
ab3721
+		else {
ab3721
+			strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
ab3721
+		}
ab3721
+		printf("%s\n", timestamp);
ab3721
+	}
ab3721
+
ab3721
+	/* Print the headings */
ab3721
+	tape_write_headings();
ab3721
+
ab3721
+	/*
ab3721
+	 * If either new or old is invalid or the I/Os per second is 0 and
ab3721
+	 * zero omit is true then we print nothing.
ab3721
+	 */
ab3721
+	if (max_tape_drives > 0) {
ab3721
+
ab3721
+		for (i = 0; i < max_tape_drives; i++) {
ab3721
+			if ((tape_new_stats[i].valid == TAPE_STATS_VALID) &&
ab3721
+				(tape_old_stats[i].valid == TAPE_STATS_VALID)) {
ab3721
+				tape_calc_one_stats(&tape, i);
ab3721
+				if (!(DISPLAY_ZERO_OMIT(flags)
ab3721
+					&& (tape.other_per_second == 0)
ab3721
+					&& (tape.reads_per_second == 0)
ab3721
+					&& (tape.writes_per_second == 0)
ab3721
+					&& (tape.kbytes_read_per_second == 0)
ab3721
+					&& (tape.kbytes_written_per_second == 0)
ab3721
+					&& (tape.read_pct_wait == 0)
ab3721
+					&& (tape.write_pct_wait == 0)
ab3721
+					&& (tape.all_pct_wait == 0)
ab3721
+					&& (tape.resids_per_second == 0))) {
ab3721
+					tape_write_stats(&tape, i);
ab3721
+				}
ab3721
+			}
ab3721
+		}
ab3721
+		/*
ab3721
+		 * Swap new and old so next time we compare against the new old stats.
ab3721
+		 * If a new tape drive appears it won't appear in the output until after
ab3721
+		 * the second time we gather information about it.
ab3721
+		 */
ab3721
+		tmp = tape_old_stats;
ab3721
+		tape_old_stats = tape_new_stats;
ab3721
+		tape_new_stats = tmp;
ab3721
+	}
ab3721
+	printf("\n");
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Main loop: Read tape stats from the relevant sources and display them.
ab3721
+ *
ab3721
+ * IN:
ab3721
+ * @count	Number of lines of stats to print.
ab3721
+ * @rectime	Current date and time.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+void rw_tape_stat_loop(long int count, struct tm *rectime)
ab3721
+{
ab3721
+	struct tape_stats *tmp;
ab3721
+	int skip = 0;
ab3721
+
ab3721
+	/* Should we skip first report? */
ab3721
+	if (DISPLAY_OMIT_SINCE_BOOT(flags) && interval > 0) {
ab3721
+		skip = 1;
ab3721
+	}
ab3721
+
ab3721
+	/* Don't buffer data if redirected to a pipe */
ab3721
+	setbuf(stdout, NULL);
ab3721
+
ab3721
+	do {
ab3721
+
ab3721
+		if (tape_new_stats == NULL) {
ab3721
+			tape_gather_initial_stats();
ab3721
+		} else {
ab3721
+			tape_get_updated_stats();
ab3721
+		}
ab3721
+
ab3721
+		/* Get time */
ab3721
+		get_localtime(rectime, 0);
ab3721
+
ab3721
+		/* Check whether we should skip first report */
ab3721
+		if (!skip) {
ab3721
+			/* Print results */
ab3721
+			write_stats(rectime);
ab3721
+
ab3721
+			if (count > 0) {
ab3721
+				count--;
ab3721
+			}
ab3721
+		}
ab3721
+		else {
ab3721
+			skip = 0;
ab3721
+			tmp = tape_old_stats;
ab3721
+			tape_old_stats = tape_new_stats;
ab3721
+			tape_new_stats = tmp;
ab3721
+		}
ab3721
+
ab3721
+		if (count) {
ab3721
+			pause();
ab3721
+		}
ab3721
+	}
ab3721
+	while (count);
ab3721
+}
ab3721
+
ab3721
+/*
ab3721
+ ***************************************************************************
ab3721
+ * Main entry to the tapestat program.
ab3721
+ ***************************************************************************
ab3721
+ */
ab3721
+int main(int argc, char **argv)
ab3721
+{
ab3721
+	int it = 0;
ab3721
+	int opt = 1;
ab3721
+	int i;
ab3721
+	long count = 1;
ab3721
+	struct utsname header;
ab3721
+	struct tm rectime;
ab3721
+
ab3721
+#ifdef USE_NLS
ab3721
+	/* Init National Language Support */
ab3721
+	init_nls();
ab3721
+#endif
ab3721
+
ab3721
+	/* Get HZ */
ab3721
+	get_HZ();
ab3721
+
ab3721
+	/* Process args... */
ab3721
+	while (opt < argc) {
ab3721
+			if (!strncmp(argv[opt], "-", 1)) {
ab3721
+			for (i = 1; *(argv[opt] + i); i++) {
ab3721
+
ab3721
+				switch (*(argv[opt] + i)) {
ab3721
+
ab3721
+				case 'k':
ab3721
+					if (DISPLAY_MEGABYTES(flags)) {
ab3721
+						usage(argv[0]);
ab3721
+					}
ab3721
+					/* Display stats in kB/s */
ab3721
+					flags |= T_D_KILOBYTES;
ab3721
+					break;
ab3721
+
ab3721
+				case 'm':
ab3721
+					if (DISPLAY_KILOBYTES(flags)) {
ab3721
+						usage(argv[0]);
ab3721
+					}
ab3721
+					/* Display stats in MB/s */
ab3721
+					flags |= T_D_MEGABYTES;
ab3721
+					break;
ab3721
+
ab3721
+				case 't':
ab3721
+					/* Display timestamp */
ab3721
+					flags |= T_D_TIMESTAMP;
ab3721
+					break;
ab3721
+
ab3721
+				case 'y':
ab3721
+					/* Don't display stats since system restart */
ab3721
+					flags |= T_D_OMIT_SINCE_BOOT;
ab3721
+					break;
ab3721
+
ab3721
+				case 'z':
ab3721
+					/* Omit output for devices with no activity */
ab3721
+					flags |= T_D_ZERO_OMIT;
ab3721
+					break;
ab3721
+
ab3721
+				case 'V':
ab3721
+					/* Print version number and exit */
ab3721
+					print_version();
ab3721
+					break;
ab3721
+
ab3721
+				default:
ab3721
+					usage(argv[0]);
ab3721
+				}
ab3721
+			}
ab3721
+			opt++;
ab3721
+		}
ab3721
+
ab3721
+		else if (!it) {
ab3721
+			interval = atol(argv[opt++]);
ab3721
+			if (interval < 0) {
ab3721
+				usage(argv[0]);
ab3721
+			}
ab3721
+			count = -1;
ab3721
+			it = 1;
ab3721
+		}
ab3721
+
ab3721
+		else if (it > 0) {
ab3721
+			count = atol(argv[opt++]);
ab3721
+			if ((count < 1) || !interval) {
ab3721
+				usage(argv[0]);
ab3721
+			}
ab3721
+			it = -1;
ab3721
+		}
ab3721
+		else {
ab3721
+			usage(argv[0]);
ab3721
+		}
ab3721
+	}
ab3721
+
ab3721
+	if (!interval) {
ab3721
+		count = 1;
ab3721
+	}
ab3721
+
ab3721
+	tape_initialise();
ab3721
+
ab3721
+	get_localtime(&rectime, 0);
ab3721
+
ab3721
+	/* Get system name, release number and hostname */
ab3721
+	uname(&header);
ab3721
+	if (print_gal_header(&rectime, header.sysname, header.release,
ab3721
+			     header.nodename, header.machine, cpu_nr)) {
ab3721
+		flags |= T_D_ISO;
ab3721
+	}
ab3721
+	printf("\n");
ab3721
+
ab3721
+	/* Set a handler for SIGALRM */
ab3721
+	memset(&alrm_act, 0, sizeof(alrm_act));
ab3721
+	alrm_act.sa_handler = alarm_handler;
ab3721
+	sigaction(SIGALRM, &alrm_act, NULL);
ab3721
+	alarm(interval);
ab3721
+
ab3721
+	/* Main loop */
ab3721
+	rw_tape_stat_loop(count, &rectime);
ab3721
+
ab3721
+	/* Free structures */
ab3721
+	tape_uninitialise();
ab3721
+
ab3721
+	return 0;
ab3721
+}
ab3721
diff -uprN sysstat-10.1.5.orig/tapestat.h sysstat-10.1.5/tapestat.h
ab3721
--- sysstat-10.1.5.orig/tapestat.h	1970-01-01 01:00:00.000000000 +0100
ab3721
+++ sysstat-10.1.5/tapestat.h	2016-06-01 12:46:52.028338551 +0200
ab3721
@@ -0,0 +1,126 @@
ab3721
+/*
ab3721
+ * tapestat: report tape statistics
ab3721
+ * (C) 2015 Hewlett-Packard Development Company, L.P.
ab3721
+ * 
ab3721
+ * Initial revision by Shane M. SEYMOUR (shane.seymour <at> hpe.com)
ab3721
+ * Modified for sysstat by Sebastien GODARD (sysstat <at> orange.fr)
ab3721
+ */
ab3721
+
ab3721
+#ifndef _TAPESTAT_H
ab3721
+#define _TAPESTAT_H
ab3721
+
ab3721
+/* T_: tapestat - D_: Display - F_: Flag */
ab3721
+#define T_D_TIMESTAMP		0x00001
ab3721
+#define T_D_KILOBYTES		0x00002
ab3721
+#define T_D_MEGABYTES		0x00004
ab3721
+#define T_D_OMIT_SINCE_BOOT	0x00008
ab3721
+#define T_D_ISO			0x00010
ab3721
+#define T_D_ZERO_OMIT		0x00020
ab3721
+
ab3721
+#define DISPLAY_TIMESTAMP(m)		(((m) & T_D_TIMESTAMP)       == T_D_TIMESTAMP)
ab3721
+#define DISPLAY_KILOBYTES(m)		(((m) & T_D_KILOBYTES)       == T_D_KILOBYTES)
ab3721
+#define DISPLAY_MEGABYTES(m)		(((m) & T_D_MEGABYTES)       == T_D_MEGABYTES)
ab3721
+#define DISPLAY_OMIT_SINCE_BOOT(m)	(((m) & T_D_OMIT_SINCE_BOOT) == T_D_OMIT_SINCE_BOOT)
ab3721
+#define DISPLAY_ISO(m)			(((m) & T_D_ISO)             == T_D_ISO)
ab3721
+#define DISPLAY_ZERO_OMIT(m)		(((m) & T_D_ZERO_OMIT)       == T_D_ZERO_OMIT)
ab3721
+
ab3721
+
ab3721
+#define TAPE_STATS_VALID 1
ab3721
+#define TAPE_STATS_INVALID 0
ab3721
+
ab3721
+#define SYSFS_CLASS_TAPE_DIR "/sys/class/scsi_tape"
ab3721
+#define TAPE_STAT_PATH "/sys/class/scsi_tape/st%i/stats/"
ab3721
+
ab3721
+#define TAPE_STAT_FILE_VAL(A, B)					\
ab3721
+	snprintf(filename, MAXPATHLEN, A, i);				\
ab3721
+	if ((fp = fopen(filename, "r")) != NULL) {			\
ab3721
+		if (fscanf(fp, "%"PRId64, &tape_new_stats[i].B) != 1) {	\
ab3721
+			tape_new_stats[i].valid = TAPE_STATS_INVALID;	\
ab3721
+		}							\
ab3721
+		fclose(fp);						\
ab3721
+	} else {							\
ab3721
+		tape_new_stats[i].valid = TAPE_STATS_INVALID;		\
ab3721
+		continue;						\
ab3721
+	}
ab3721
+
ab3721
+
ab3721
+/*
ab3721
+ * A - tape_stats structure member name, e.g. read_count
ab3721
+ * B - calc_stats structure member name, e.g. reads_per_second
ab3721
+ *
ab3721
+ * These macros are not selfcontained they depend on some other
ab3721
+ * variables defined either as global or local to the function.
ab3721
+ */
ab3721
+
ab3721
+#define CALC_STAT_CNT(A, B)					\
ab3721
+	if ((tape_new_stats[i].A == tape_old_stats[i].A) ||	\
ab3721
+		(duration <= 0)) {				\
ab3721
+		stats->B = 0;					\
ab3721
+	} else {						\
ab3721
+                temp = (double) (tape_new_stats[i].A -		\
ab3721
+			tape_old_stats[i].A)			\
ab3721
+			/ (((double) duration) / 1000);		\
ab3721
+		stats->B = (uint64_t) temp;			\
ab3721
+	}
ab3721
+#define CALC_STAT_KB(A, B)					\
ab3721
+        if ((tape_new_stats[i].A == tape_old_stats[i].A) ||	\
ab3721
+		(duration <= 0)) {				\
ab3721
+		stats->B = 0;					\
ab3721
+        } else {						\
ab3721
+		temp = (double) (tape_new_stats[i].A -		\
ab3721
+			tape_old_stats[i].A)			\
ab3721
+			/ (((double) duration) / 1000.0);	\
ab3721
+		stats->B = (uint64_t) (temp / 1024.0);		\
ab3721
+	}
ab3721
+
ab3721
+#define TAPE_MAX_PCT 999
ab3721
+
ab3721
+#define CALC_STAT_PCT(A, B)						\
ab3721
+	if ((tape_new_stats[i].A == tape_old_stats[i].A) ||		\
ab3721
+		(duration <= 0)) {					\
ab3721
+		stats->B = 0;						\
ab3721
+	} else {							\
ab3721
+		temp = (double) (tape_new_stats[i].A -			\
ab3721
+			tape_old_stats[i].A)				\
ab3721
+			/ (((double) duration));			\
ab3721
+		stats->B = (uint64_t) (100.0 * temp / 1000000.0);	\
ab3721
+		if (stats->B > TAPE_MAX_PCT)				\
ab3721
+			stats->B = TAPE_MAX_PCT;				\
ab3721
+	}
ab3721
+
ab3721
+struct tape_stats {
ab3721
+        uint64_t read_time;
ab3721
+        uint64_t write_time;
ab3721
+        uint64_t other_time;
ab3721
+        uint64_t read_bytes;
ab3721
+        uint64_t write_bytes;
ab3721
+        uint64_t read_count;
ab3721
+        uint64_t write_count;
ab3721
+        uint64_t other_count;
ab3721
+        uint64_t resid_count;
ab3721
+        char valid;
ab3721
+        struct timeval tv;
ab3721
+};
ab3721
+struct calc_stats {
ab3721
+        uint64_t reads_per_second;
ab3721
+        uint64_t writes_per_second;
ab3721
+        uint64_t other_per_second;
ab3721
+        uint64_t kbytes_read_per_second;
ab3721
+        uint64_t kbytes_written_per_second;
ab3721
+        uint64_t read_pct_wait;
ab3721
+        uint64_t write_pct_wait;
ab3721
+        uint64_t all_pct_wait;
ab3721
+        uint64_t resids_per_second;
ab3721
+};
ab3721
+
ab3721
+void tape_get_updated_stats(void);
ab3721
+void tape_gather_initial_stats(void);
ab3721
+void tape_check_tapes_and_realloc(void);
ab3721
+int get_max_tape_drives(void);
ab3721
+void tape_uninitialise(void);
ab3721
+void tape_initialise(void);
ab3721
+void tape_calc_one_stats(struct calc_stats *, int);
ab3721
+void tape_write_headings(void);
ab3721
+void tape_write_stats(struct calc_stats *, int);
ab3721
+
ab3721
+#endif  /* _TAPESTAT_H */