xref: /freebsd/usr.bin/ctlstat/ctlstat.c (revision 33d35bebefee000a593b1b29d1bbaecaf6717e95)
1130f4520SKenneth D. Merry /*-
2130f4520SKenneth D. Merry  * Copyright (c) 2004, 2008, 2009 Silicon Graphics International Corp.
3130f4520SKenneth D. Merry  * All rights reserved.
4130f4520SKenneth D. Merry  *
5130f4520SKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
6130f4520SKenneth D. Merry  * modification, are permitted provided that the following conditions
7130f4520SKenneth D. Merry  * are met:
8130f4520SKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright
9130f4520SKenneth D. Merry  *    notice, this list of conditions, and the following disclaimer,
10130f4520SKenneth D. Merry  *    without modification.
11130f4520SKenneth D. Merry  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12130f4520SKenneth D. Merry  *    substantially similar to the "NO WARRANTY" disclaimer below
13130f4520SKenneth D. Merry  *    ("Disclaimer") and any redistribution must be conditioned upon
14130f4520SKenneth D. Merry  *    including a substantially similar Disclaimer requirement for further
15130f4520SKenneth D. Merry  *    binary redistribution.
16130f4520SKenneth D. Merry  *
17130f4520SKenneth D. Merry  * NO WARRANTY
18130f4520SKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19130f4520SKenneth D. Merry  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20130f4520SKenneth D. Merry  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21130f4520SKenneth D. Merry  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22130f4520SKenneth D. Merry  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23130f4520SKenneth D. Merry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24130f4520SKenneth D. Merry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25130f4520SKenneth D. Merry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26130f4520SKenneth D. Merry  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27130f4520SKenneth D. Merry  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28130f4520SKenneth D. Merry  * POSSIBILITY OF SUCH DAMAGES.
29130f4520SKenneth D. Merry  *
30130f4520SKenneth D. Merry  * $Id: //depot/users/kenm/FreeBSD-test2/usr.bin/ctlstat/ctlstat.c#4 $
31130f4520SKenneth D. Merry  */
32130f4520SKenneth D. Merry /*
33130f4520SKenneth D. Merry  * CAM Target Layer statistics program
34130f4520SKenneth D. Merry  *
35130f4520SKenneth D. Merry  * Authors: Ken Merry <ken@FreeBSD.org>, Will Andrews <will@FreeBSD.org>
36130f4520SKenneth D. Merry  */
37130f4520SKenneth D. Merry 
38130f4520SKenneth D. Merry #include <sys/cdefs.h>
39130f4520SKenneth D. Merry __FBSDID("$FreeBSD$");
40130f4520SKenneth D. Merry 
41130f4520SKenneth D. Merry #include <sys/ioctl.h>
42130f4520SKenneth D. Merry #include <sys/types.h>
43130f4520SKenneth D. Merry #include <sys/param.h>
44130f4520SKenneth D. Merry #include <sys/time.h>
45130f4520SKenneth D. Merry #include <sys/sysctl.h>
46130f4520SKenneth D. Merry #include <sys/resource.h>
47130f4520SKenneth D. Merry #include <sys/queue.h>
48130f4520SKenneth D. Merry #include <sys/callout.h>
49130f4520SKenneth D. Merry #include <stdint.h>
50130f4520SKenneth D. Merry #include <stdio.h>
51130f4520SKenneth D. Merry #include <stdlib.h>
52130f4520SKenneth D. Merry #include <unistd.h>
53130f4520SKenneth D. Merry #include <fcntl.h>
54130f4520SKenneth D. Merry #include <getopt.h>
55130f4520SKenneth D. Merry #include <string.h>
56130f4520SKenneth D. Merry #include <errno.h>
57130f4520SKenneth D. Merry #include <err.h>
58130f4520SKenneth D. Merry #include <ctype.h>
59130f4520SKenneth D. Merry #include <bitstring.h>
60130f4520SKenneth D. Merry #include <cam/scsi/scsi_all.h>
61130f4520SKenneth D. Merry #include <cam/ctl/ctl.h>
62130f4520SKenneth D. Merry #include <cam/ctl/ctl_io.h>
63130f4520SKenneth D. Merry #include <cam/ctl/ctl_scsi_all.h>
64130f4520SKenneth D. Merry #include <cam/ctl/ctl_util.h>
65130f4520SKenneth D. Merry #include <cam/ctl/ctl_frontend_internal.h>
66130f4520SKenneth D. Merry #include <cam/ctl/ctl_backend.h>
67130f4520SKenneth D. Merry #include <cam/ctl/ctl_ioctl.h>
68130f4520SKenneth D. Merry 
69130f4520SKenneth D. Merry /*
70130f4520SKenneth D. Merry  * The default amount of space we allocate for LUN storage space.  We
71130f4520SKenneth D. Merry  * dynamically allocate more if needed.
72130f4520SKenneth D. Merry  */
73130f4520SKenneth D. Merry #define	CTL_STAT_NUM_LUNS	30
74130f4520SKenneth D. Merry 
75130f4520SKenneth D. Merry /*
76130f4520SKenneth D. Merry  * The default number of LUN selection bits we allocate.  This is large
77130f4520SKenneth D. Merry  * because we don't currently increase it if the user specifies a LUN
78130f4520SKenneth D. Merry  * number of 1024 or larger.
79130f4520SKenneth D. Merry  */
80130f4520SKenneth D. Merry #define	CTL_STAT_LUN_BITS	1024L
81130f4520SKenneth D. Merry 
82130f4520SKenneth D. Merry static const char *ctlstat_opts = "Cc:Ddhjl:n:tw:";
83130f4520SKenneth D. Merry static const char *ctlstat_usage = "Usage:  ctlstat [-CDdjht] [-l lunnum]"
84130f4520SKenneth D. Merry 				   "[-c count] [-n numdevs] [-w wait]\n";
85130f4520SKenneth D. Merry 
86130f4520SKenneth D. Merry struct ctl_cpu_stats {
87130f4520SKenneth D. Merry 	uint64_t user;
88130f4520SKenneth D. Merry 	uint64_t nice;
89130f4520SKenneth D. Merry 	uint64_t system;
90130f4520SKenneth D. Merry 	uint64_t intr;
91130f4520SKenneth D. Merry 	uint64_t idle;
92130f4520SKenneth D. Merry };
93130f4520SKenneth D. Merry 
94130f4520SKenneth D. Merry typedef enum {
95130f4520SKenneth D. Merry 	CTLSTAT_MODE_STANDARD,
96130f4520SKenneth D. Merry 	CTLSTAT_MODE_DUMP,
97130f4520SKenneth D. Merry 	CTLSTAT_MODE_JSON,
98130f4520SKenneth D. Merry } ctlstat_mode_types;
99130f4520SKenneth D. Merry 
100130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_CPU		(1 << 0)
101130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_HEADER		(1 << 1)
102130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_FIRST_RUN		(1 << 2)
103130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_TOTALS		(1 << 3)
104130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_DMA_TIME		(1 << 4)
105130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_LUN_TIME_VALID	(1 << 5)
106130f4520SKenneth D. Merry #define	F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU)
107130f4520SKenneth D. Merry #define	F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER)
108130f4520SKenneth D. Merry #define	F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN)
109130f4520SKenneth D. Merry #define	F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS)
110130f4520SKenneth D. Merry #define	F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME)
111130f4520SKenneth D. Merry #define	F_LUNVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUN_TIME_VALID)
112130f4520SKenneth D. Merry 
113130f4520SKenneth D. Merry struct ctlstat_context {
114130f4520SKenneth D. Merry 	ctlstat_mode_types mode;
115130f4520SKenneth D. Merry 	int flags;
116130f4520SKenneth D. Merry 	struct ctl_lun_io_stats *cur_lun_stats, *prev_lun_stats,
117130f4520SKenneth D. Merry 		*tmp_lun_stats;
118130f4520SKenneth D. Merry 	struct ctl_lun_io_stats cur_total_stats[3], prev_total_stats[3];
119130f4520SKenneth D. Merry 	struct timespec cur_time, prev_time;
120130f4520SKenneth D. Merry 	struct ctl_cpu_stats cur_cpu, prev_cpu;
121130f4520SKenneth D. Merry 	uint64_t cur_total_jiffies, prev_total_jiffies;
122130f4520SKenneth D. Merry 	uint64_t cur_idle, prev_idle;
123130f4520SKenneth D. Merry 	bitstr_t bit_decl(lun_mask, CTL_STAT_LUN_BITS);
124130f4520SKenneth D. Merry 	int num_luns;
125130f4520SKenneth D. Merry 	int numdevs;
126130f4520SKenneth D. Merry 	int header_interval;
127130f4520SKenneth D. Merry };
128130f4520SKenneth D. Merry 
129130f4520SKenneth D. Merry #ifndef min
130130f4520SKenneth D. Merry #define	min(x,y)	(((x) < (y)) ? (x) : (y))
131130f4520SKenneth D. Merry #endif
132130f4520SKenneth D. Merry 
133130f4520SKenneth D. Merry static void usage(int error);
134130f4520SKenneth D. Merry static int getstats(int fd, int *num_luns, struct ctl_lun_io_stats **xlun_stats,
135130f4520SKenneth D. Merry 		    struct timespec *cur_time, int *lun_time_valid);
136130f4520SKenneth D. Merry static int getcpu(struct ctl_cpu_stats *cpu_stats);
137130f4520SKenneth D. Merry static void compute_stats(struct ctl_lun_io_stats *cur_stats,
138130f4520SKenneth D. Merry 			  struct ctl_lun_io_stats *prev_stats,
139130f4520SKenneth D. Merry 			  long double etime, long double *mbsec,
140130f4520SKenneth D. Merry 			  long double *kb_per_transfer,
141130f4520SKenneth D. Merry 			  long double *transfers_per_second,
142130f4520SKenneth D. Merry 			  long double *ms_per_transfer,
143130f4520SKenneth D. Merry 			  long double *ms_per_dma,
144130f4520SKenneth D. Merry 			  long double *dmas_per_second);
145130f4520SKenneth D. Merry 
146130f4520SKenneth D. Merry static void
147130f4520SKenneth D. Merry usage(int error)
148130f4520SKenneth D. Merry {
149*33d35bebSKenneth D. Merry 	fputs(ctlstat_usage, error ? stderr : stdout);
150130f4520SKenneth D. Merry }
151130f4520SKenneth D. Merry 
152130f4520SKenneth D. Merry static int
153130f4520SKenneth D. Merry getstats(int fd, int *num_luns, struct ctl_lun_io_stats **xlun_stats,
154130f4520SKenneth D. Merry 	 struct timespec *cur_time, int *flags)
155130f4520SKenneth D. Merry {
156130f4520SKenneth D. Merry 	struct ctl_lun_io_stats *lun_stats;
157130f4520SKenneth D. Merry 	struct ctl_stats stats;
158130f4520SKenneth D. Merry 	int more_space_count;
159130f4520SKenneth D. Merry 
160130f4520SKenneth D. Merry 	more_space_count = 0;
161130f4520SKenneth D. Merry 
162130f4520SKenneth D. Merry 	if (*num_luns == 0)
163130f4520SKenneth D. Merry 		*num_luns = CTL_STAT_NUM_LUNS;
164130f4520SKenneth D. Merry 
165130f4520SKenneth D. Merry 	lun_stats = *xlun_stats;
166130f4520SKenneth D. Merry retry:
167130f4520SKenneth D. Merry 
168130f4520SKenneth D. Merry 	if (lun_stats == NULL) {
169130f4520SKenneth D. Merry 		lun_stats = (struct ctl_lun_io_stats *)malloc(
170130f4520SKenneth D. Merry 			sizeof(*lun_stats) * *num_luns);
171130f4520SKenneth D. Merry 	}
172130f4520SKenneth D. Merry 
173130f4520SKenneth D. Merry 	memset(&stats, 0, sizeof(stats));
174130f4520SKenneth D. Merry 	stats.alloc_len = *num_luns * sizeof(*lun_stats);
175130f4520SKenneth D. Merry 	memset(lun_stats, 0, stats.alloc_len);
176130f4520SKenneth D. Merry 	stats.lun_stats = lun_stats;
177130f4520SKenneth D. Merry 
178130f4520SKenneth D. Merry 	if (ioctl(fd, CTL_GETSTATS, &stats) == -1)
179130f4520SKenneth D. Merry 		err(1, "error returned from CTL_GETSTATS ioctl");
180130f4520SKenneth D. Merry 
181130f4520SKenneth D. Merry 	switch (stats.status) {
182130f4520SKenneth D. Merry 	case CTL_SS_OK:
183130f4520SKenneth D. Merry 		break;
184130f4520SKenneth D. Merry 	case CTL_SS_ERROR:
185130f4520SKenneth D. Merry 		err(1, "CTL_SS_ERROR returned from CTL_GETSTATS ioctl");
186130f4520SKenneth D. Merry 		break;
187130f4520SKenneth D. Merry 	case CTL_SS_NEED_MORE_SPACE:
188130f4520SKenneth D. Merry 		if (more_space_count > 0) {
189130f4520SKenneth D. Merry 			errx(1, "CTL_GETSTATS returned NEED_MORE_SPACE again");
190130f4520SKenneth D. Merry 		}
191130f4520SKenneth D. Merry 		*num_luns = stats.num_luns;
192130f4520SKenneth D. Merry 		free(lun_stats);
193130f4520SKenneth D. Merry 		lun_stats = NULL;
194130f4520SKenneth D. Merry 		more_space_count++;
195130f4520SKenneth D. Merry 		goto retry;
196130f4520SKenneth D. Merry 		break; /* NOTREACHED */
197130f4520SKenneth D. Merry 	default:
198130f4520SKenneth D. Merry 		errx(1, "unknown status %d returned from CTL_GETSTATS ioctl",
199130f4520SKenneth D. Merry 		     stats.status);
200130f4520SKenneth D. Merry 		break;
201130f4520SKenneth D. Merry 	}
202130f4520SKenneth D. Merry 
203130f4520SKenneth D. Merry 	*xlun_stats = lun_stats;
204130f4520SKenneth D. Merry 	*num_luns = stats.num_luns;
205130f4520SKenneth D. Merry 	cur_time->tv_sec = stats.timestamp.tv_sec;
206130f4520SKenneth D. Merry 	cur_time->tv_nsec = stats.timestamp.tv_nsec;
207130f4520SKenneth D. Merry 	if (stats.flags & CTL_STATS_FLAG_TIME_VALID)
208130f4520SKenneth D. Merry 		*flags |= CTLSTAT_FLAG_LUN_TIME_VALID;
209130f4520SKenneth D. Merry 	else
210130f4520SKenneth D. Merry 		*flags &= ~CTLSTAT_FLAG_LUN_TIME_VALID;
211130f4520SKenneth D. Merry 
212130f4520SKenneth D. Merry 	return (0);
213130f4520SKenneth D. Merry }
214130f4520SKenneth D. Merry 
215130f4520SKenneth D. Merry static int
216130f4520SKenneth D. Merry getcpu(struct ctl_cpu_stats *cpu_stats)
217130f4520SKenneth D. Merry {
218130f4520SKenneth D. Merry 	long cp_time[CPUSTATES];
219130f4520SKenneth D. Merry 	size_t cplen;
220130f4520SKenneth D. Merry 
221130f4520SKenneth D. Merry 	cplen = sizeof(cp_time);
222130f4520SKenneth D. Merry 
223130f4520SKenneth D. Merry 	if (sysctlbyname("kern.cp_time", &cp_time, &cplen, NULL, 0) == -1) {
224130f4520SKenneth D. Merry 		warn("sysctlbyname(kern.cp_time...) failed");
225130f4520SKenneth D. Merry 		return (1);
226130f4520SKenneth D. Merry 	}
227130f4520SKenneth D. Merry 
228130f4520SKenneth D. Merry 	cpu_stats->user = cp_time[CP_USER];
229130f4520SKenneth D. Merry 	cpu_stats->nice = cp_time[CP_NICE];
230130f4520SKenneth D. Merry 	cpu_stats->system = cp_time[CP_SYS];
231130f4520SKenneth D. Merry 	cpu_stats->intr = cp_time[CP_INTR];
232130f4520SKenneth D. Merry 	cpu_stats->idle = cp_time[CP_IDLE];
233130f4520SKenneth D. Merry 
234130f4520SKenneth D. Merry 	return (0);
235130f4520SKenneth D. Merry }
236130f4520SKenneth D. Merry 
237130f4520SKenneth D. Merry static void
238130f4520SKenneth D. Merry compute_stats(struct ctl_lun_io_stats *cur_stats,
239130f4520SKenneth D. Merry 	      struct ctl_lun_io_stats *prev_stats, long double etime,
240130f4520SKenneth D. Merry 	      long double *mbsec, long double *kb_per_transfer,
241130f4520SKenneth D. Merry 	      long double *transfers_per_second, long double *ms_per_transfer,
242130f4520SKenneth D. Merry 	      long double *ms_per_dma, long double *dmas_per_second)
243130f4520SKenneth D. Merry {
244130f4520SKenneth D. Merry 	uint64_t total_bytes = 0, total_operations = 0, total_dmas = 0;
245130f4520SKenneth D. Merry 	uint32_t port;
246130f4520SKenneth D. Merry 	struct bintime total_time_bt, total_dma_bt;
247130f4520SKenneth D. Merry 	struct timespec total_time_ts, total_dma_ts;
248130f4520SKenneth D. Merry 	int i;
249130f4520SKenneth D. Merry 
250130f4520SKenneth D. Merry 	bzero(&total_time_bt, sizeof(total_time_bt));
251130f4520SKenneth D. Merry 	bzero(&total_dma_bt, sizeof(total_dma_bt));
252130f4520SKenneth D. Merry 	bzero(&total_time_ts, sizeof(total_time_ts));
253130f4520SKenneth D. Merry 	bzero(&total_dma_ts, sizeof(total_dma_ts));
254130f4520SKenneth D. Merry 	for (port = 0; port < CTL_MAX_PORTS; port++) {
255130f4520SKenneth D. Merry 		for (i = 0; i < CTL_STATS_NUM_TYPES; i++) {
256130f4520SKenneth D. Merry 			total_bytes += cur_stats->ports[port].bytes[i];
257130f4520SKenneth D. Merry 			total_operations +=
258130f4520SKenneth D. Merry 			    cur_stats->ports[port].operations[i];
259130f4520SKenneth D. Merry 			total_dmas += cur_stats->ports[port].num_dmas[i];
260130f4520SKenneth D. Merry 			bintime_add(&total_time_bt,
261130f4520SKenneth D. Merry 			    &cur_stats->ports[port].time[i]);
262130f4520SKenneth D. Merry 			bintime_add(&total_dma_bt,
263130f4520SKenneth D. Merry 			    &cur_stats->ports[port].dma_time[i]);
264130f4520SKenneth D. Merry 			if (prev_stats != NULL) {
265130f4520SKenneth D. Merry 				total_bytes -=
266130f4520SKenneth D. Merry 				    prev_stats->ports[port].bytes[i];
267130f4520SKenneth D. Merry 				total_operations -=
268130f4520SKenneth D. Merry 				    prev_stats->ports[port].operations[i];
269130f4520SKenneth D. Merry 				total_dmas -=
270130f4520SKenneth D. Merry 				    prev_stats->ports[port].num_dmas[i];
271130f4520SKenneth D. Merry 				bintime_sub(&total_time_bt,
272130f4520SKenneth D. Merry 				    &prev_stats->ports[port].time[i]);
273130f4520SKenneth D. Merry 				bintime_sub(&total_dma_bt,
274130f4520SKenneth D. Merry 				    &prev_stats->ports[port].dma_time[i]);
275130f4520SKenneth D. Merry 			}
276130f4520SKenneth D. Merry 		}
277130f4520SKenneth D. Merry 	}
278130f4520SKenneth D. Merry 
279130f4520SKenneth D. Merry 	*mbsec = total_bytes;
280130f4520SKenneth D. Merry 	*mbsec /= 1024 * 1024;
281130f4520SKenneth D. Merry 	if (etime > 0.0)
282130f4520SKenneth D. Merry 		*mbsec /= etime;
283130f4520SKenneth D. Merry 	else
284130f4520SKenneth D. Merry 		*mbsec = 0;
285130f4520SKenneth D. Merry 	*kb_per_transfer = total_bytes;
286130f4520SKenneth D. Merry 	*kb_per_transfer /= 1024;
287130f4520SKenneth D. Merry 	if (total_operations > 0)
288130f4520SKenneth D. Merry 		*kb_per_transfer /= total_operations;
289130f4520SKenneth D. Merry 	else
290130f4520SKenneth D. Merry 		*kb_per_transfer = 0;
291130f4520SKenneth D. Merry 	*transfers_per_second = total_operations;
292130f4520SKenneth D. Merry 	*dmas_per_second = total_dmas;
293130f4520SKenneth D. Merry 	if (etime > 0.0) {
294130f4520SKenneth D. Merry 		*transfers_per_second /= etime;
295130f4520SKenneth D. Merry 		*dmas_per_second /= etime;
296130f4520SKenneth D. Merry 	} else {
297130f4520SKenneth D. Merry 		*transfers_per_second = 0;
298130f4520SKenneth D. Merry 		*dmas_per_second = 0;
299130f4520SKenneth D. Merry 	}
300130f4520SKenneth D. Merry 
301130f4520SKenneth D. Merry 	bintime2timespec(&total_time_bt, &total_time_ts);
302130f4520SKenneth D. Merry 	bintime2timespec(&total_dma_bt, &total_dma_ts);
303130f4520SKenneth D. Merry 	if (total_operations > 0) {
304130f4520SKenneth D. Merry 		/*
305130f4520SKenneth D. Merry 		 * Convert the timespec to milliseconds.
306130f4520SKenneth D. Merry 		 */
307130f4520SKenneth D. Merry 		*ms_per_transfer = total_time_ts.tv_sec * 1000;
308130f4520SKenneth D. Merry 		*ms_per_transfer += total_time_ts.tv_nsec / 1000000;
309130f4520SKenneth D. Merry 		*ms_per_transfer /= total_operations;
310130f4520SKenneth D. Merry 	} else
311130f4520SKenneth D. Merry 		*ms_per_transfer = 0;
312130f4520SKenneth D. Merry 
313130f4520SKenneth D. Merry 	if (total_dmas > 0) {
314130f4520SKenneth D. Merry 		/*
315130f4520SKenneth D. Merry 		 * Convert the timespec to milliseconds.
316130f4520SKenneth D. Merry 		 */
317130f4520SKenneth D. Merry 		*ms_per_dma = total_dma_ts.tv_sec * 1000;
318130f4520SKenneth D. Merry 		*ms_per_dma += total_dma_ts.tv_nsec / 1000000;
319130f4520SKenneth D. Merry 		*ms_per_dma /= total_dmas;
320130f4520SKenneth D. Merry 	} else
321130f4520SKenneth D. Merry 		*ms_per_dma = 0;
322130f4520SKenneth D. Merry }
323130f4520SKenneth D. Merry 
324130f4520SKenneth D. Merry /* The dump_stats() and json_stats() functions perform essentially the same
325130f4520SKenneth D. Merry  * purpose, but dump the statistics in different formats.  JSON is more
326130f4520SKenneth D. Merry  * conducive to programming, however.
327130f4520SKenneth D. Merry  */
328130f4520SKenneth D. Merry 
329130f4520SKenneth D. Merry #define	PRINT_BINTIME(prefix, bt) \
330130f4520SKenneth D. Merry 	printf("%s %jd s %ju frac\n", prefix, (intmax_t)(bt).sec, \
331130f4520SKenneth D. Merry 	       (uintmax_t)(bt).frac)
332130f4520SKenneth D. Merry const char *iotypes[] = {"NO IO", "READ", "WRITE"};
333130f4520SKenneth D. Merry 
334130f4520SKenneth D. Merry static void
335130f4520SKenneth D. Merry ctlstat_dump(struct ctlstat_context *ctx) {
336130f4520SKenneth D. Merry 	int iotype, lun, port;
337130f4520SKenneth D. Merry 	struct ctl_lun_io_stats *stats = ctx->cur_lun_stats;
338130f4520SKenneth D. Merry 
339130f4520SKenneth D. Merry 	for (lun = 0; lun < ctx->num_luns;lun++) {
340130f4520SKenneth D. Merry 		printf("lun %d\n", lun);
341130f4520SKenneth D. Merry 		for (port = 0; port < CTL_MAX_PORTS; port++) {
342130f4520SKenneth D. Merry 			printf(" port %d\n",
343130f4520SKenneth D. Merry 			    stats[lun].ports[port].targ_port);
344130f4520SKenneth D. Merry 			for (iotype = 0; iotype < CTL_STATS_NUM_TYPES;
345130f4520SKenneth D. Merry 			    iotype++) {
346130f4520SKenneth D. Merry 				printf("  io type %d (%s)\n", iotype,
347130f4520SKenneth D. Merry 				    iotypes[iotype]);
348130f4520SKenneth D. Merry 				printf("   bytes %ju\n", (uintmax_t)
349130f4520SKenneth D. Merry 				    stats[lun].ports[port].bytes[iotype]);
350130f4520SKenneth D. Merry 				printf("   operations %ju\n", (uintmax_t)
351130f4520SKenneth D. Merry 				    stats[lun].ports[port].operations[iotype]);
352130f4520SKenneth D. Merry 				PRINT_BINTIME("   io time",
353130f4520SKenneth D. Merry 				    stats[lun].ports[port].time[iotype]);
354130f4520SKenneth D. Merry 				printf("   num dmas %ju\n", (uintmax_t)
355130f4520SKenneth D. Merry 				    stats[lun].ports[port].num_dmas[iotype]);
356130f4520SKenneth D. Merry 				PRINT_BINTIME("   dma time",
357130f4520SKenneth D. Merry 				    stats[lun].ports[port].dma_time[iotype]);
358130f4520SKenneth D. Merry 			}
359130f4520SKenneth D. Merry 		}
360130f4520SKenneth D. Merry 	}
361130f4520SKenneth D. Merry }
362130f4520SKenneth D. Merry 
363130f4520SKenneth D. Merry #define	JSON_BINTIME(prefix, bt) \
364130f4520SKenneth D. Merry 	printf("\"%s\":{\"sec\":%jd,\"frac\":%ju},", \
365130f4520SKenneth D. Merry 	    prefix, (intmax_t)(bt).sec, (uintmax_t)(bt).frac)
366130f4520SKenneth D. Merry 
367130f4520SKenneth D. Merry static void
368130f4520SKenneth D. Merry ctlstat_json(struct ctlstat_context *ctx) {
369130f4520SKenneth D. Merry 	int iotype, lun, port;
370130f4520SKenneth D. Merry 	struct ctl_lun_io_stats *stats = ctx->cur_lun_stats;
371130f4520SKenneth D. Merry 
372130f4520SKenneth D. Merry 	printf("{\"luns\":[");
373130f4520SKenneth D. Merry 	for (lun = 0; lun < ctx->num_luns; lun++) {
374130f4520SKenneth D. Merry 		printf("{\"ports\":[");
375130f4520SKenneth D. Merry 		for (port = 0; port < CTL_MAX_PORTS;port++) {
376130f4520SKenneth D. Merry 			printf("{\"num\":%d,\"io\":[",
377130f4520SKenneth D. Merry 			    stats[lun].ports[port].targ_port);
378130f4520SKenneth D. Merry 			for (iotype = 0; iotype < CTL_STATS_NUM_TYPES;
379130f4520SKenneth D. Merry 			    iotype++) {
380130f4520SKenneth D. Merry 				printf("{\"type\":\"%s\",", iotypes[iotype]);
381130f4520SKenneth D. Merry 				printf("\"bytes\":%ju,", (uintmax_t)stats[
382130f4520SKenneth D. Merry 				       lun].ports[port].bytes[iotype]);
383130f4520SKenneth D. Merry 				printf("\"operations\":%ju,", (uintmax_t)stats[
384130f4520SKenneth D. Merry 				       lun].ports[port].operations[iotype]);
385130f4520SKenneth D. Merry 				JSON_BINTIME("io time",
386130f4520SKenneth D. Merry 				    stats[lun].ports[port].time[iotype]);
387130f4520SKenneth D. Merry 				JSON_BINTIME("dma time",
388130f4520SKenneth D. Merry 				    stats[lun].ports[port].dma_time[iotype]);
389130f4520SKenneth D. Merry 				printf("\"num dmas\":%ju}", (uintmax_t)
390130f4520SKenneth D. Merry 				    stats[lun].ports[port].num_dmas[iotype]);
391130f4520SKenneth D. Merry 				if (iotype < (CTL_STATS_NUM_TYPES - 1))
392130f4520SKenneth D. Merry 					printf(","); /* continue io array */
393130f4520SKenneth D. Merry 			}
394130f4520SKenneth D. Merry 			printf("]}"); /* close port */
395130f4520SKenneth D. Merry 			if (port < (CTL_MAX_PORTS - 1))
396130f4520SKenneth D. Merry 				printf(","); /* continue port array */
397130f4520SKenneth D. Merry 		}
398130f4520SKenneth D. Merry 		printf("]}"); /* close lun */
399130f4520SKenneth D. Merry 		if (lun < (ctx->num_luns - 1))
400130f4520SKenneth D. Merry 			printf(","); /* continue lun array */
401130f4520SKenneth D. Merry 	}
402130f4520SKenneth D. Merry 	printf("]}"); /* close luns and toplevel */
403130f4520SKenneth D. Merry }
404130f4520SKenneth D. Merry 
405130f4520SKenneth D. Merry static void
406130f4520SKenneth D. Merry ctlstat_standard(struct ctlstat_context *ctx) {
407130f4520SKenneth D. Merry 	long double cur_secs, prev_secs, etime;
408130f4520SKenneth D. Merry 	uint64_t delta_jiffies, delta_idle;
409130f4520SKenneth D. Merry 	uint32_t port;
410130f4520SKenneth D. Merry 	long double cpu_percentage;
411130f4520SKenneth D. Merry 	int i;
412130f4520SKenneth D. Merry 	int j;
413130f4520SKenneth D. Merry 
414130f4520SKenneth D. Merry 	cpu_percentage = 0;
415130f4520SKenneth D. Merry 
416130f4520SKenneth D. Merry 	if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0))
417130f4520SKenneth D. Merry 		errx(1, "error returned from getcpu()");
418130f4520SKenneth D. Merry 
419130f4520SKenneth D. Merry 	cur_secs = ctx->cur_time.tv_sec + (ctx->cur_time.tv_nsec / 1000000000);
420130f4520SKenneth D. Merry 	prev_secs = ctx->prev_time.tv_sec +
421130f4520SKenneth D. Merry 	     (ctx->prev_time.tv_nsec / 1000000000);
422130f4520SKenneth D. Merry 
423130f4520SKenneth D. Merry 	etime = cur_secs - prev_secs;
424130f4520SKenneth D. Merry 
425130f4520SKenneth D. Merry 	if (F_CPU(ctx)) {
426130f4520SKenneth D. Merry 		ctx->prev_total_jiffies = ctx->cur_total_jiffies;
427130f4520SKenneth D. Merry 		ctx->cur_total_jiffies = ctx->cur_cpu.user +
428130f4520SKenneth D. Merry 		    ctx->cur_cpu.nice + ctx->cur_cpu.system +
429130f4520SKenneth D. Merry 		    ctx->cur_cpu.intr + ctx->cur_cpu.idle;
430130f4520SKenneth D. Merry 		delta_jiffies = ctx->cur_total_jiffies;
431130f4520SKenneth D. Merry 		if (F_FIRST(ctx) == 0)
432130f4520SKenneth D. Merry 			delta_jiffies -= ctx->prev_total_jiffies;
433130f4520SKenneth D. Merry 		ctx->prev_idle = ctx->cur_idle;
434130f4520SKenneth D. Merry 		ctx->cur_idle = ctx->cur_cpu.idle;
435130f4520SKenneth D. Merry 		delta_idle = ctx->cur_idle - ctx->prev_idle;
436130f4520SKenneth D. Merry 
437130f4520SKenneth D. Merry 		cpu_percentage = delta_jiffies - delta_idle;
438130f4520SKenneth D. Merry 		cpu_percentage /= delta_jiffies;
439130f4520SKenneth D. Merry 		cpu_percentage *= 100;
440130f4520SKenneth D. Merry 	}
441130f4520SKenneth D. Merry 
442130f4520SKenneth D. Merry 	if (F_HDR(ctx)) {
443130f4520SKenneth D. Merry 		ctx->header_interval--;
444130f4520SKenneth D. Merry 		if (ctx->header_interval <= 0) {
445130f4520SKenneth D. Merry 			int hdr_devs;
446130f4520SKenneth D. Merry 
447130f4520SKenneth D. Merry 			hdr_devs = 0;
448130f4520SKenneth D. Merry 
449130f4520SKenneth D. Merry 			if (F_TOTALS(ctx)) {
450130f4520SKenneth D. Merry 				fprintf(stdout, "%s     System Read     %s"
451130f4520SKenneth D. Merry 					"System Write     %sSystem Total%s\n",
452130f4520SKenneth D. Merry 					(F_LUNVAL(ctx) != 0) ? "     " : "",
453130f4520SKenneth D. Merry 					(F_LUNVAL(ctx) != 0) ? "     " : "",
454130f4520SKenneth D. Merry 					(F_LUNVAL(ctx) != 0) ? "     " : "",
455130f4520SKenneth D. Merry 					(F_CPU(ctx) == 0)   ? "    CPU" : "");
456130f4520SKenneth D. Merry 				hdr_devs = 3;
457130f4520SKenneth D. Merry 			} else {
458130f4520SKenneth D. Merry 				if (F_CPU(ctx))
459130f4520SKenneth D. Merry 					fprintf(stdout, "  CPU  ");
460130f4520SKenneth D. Merry 				for (i = 0; i < min(CTL_STAT_LUN_BITS,
461130f4520SKenneth D. Merry 				     ctx->num_luns); i++) {
462130f4520SKenneth D. Merry 					int lun;
463130f4520SKenneth D. Merry 
464130f4520SKenneth D. Merry 					/*
465130f4520SKenneth D. Merry 					 * Obviously this won't work with
466130f4520SKenneth D. Merry 					 * LUN numbers greater than a signed
467130f4520SKenneth D. Merry 					 * integer.
468130f4520SKenneth D. Merry 					 */
469130f4520SKenneth D. Merry 					lun = (int)ctx->cur_lun_stats[i
470130f4520SKenneth D. Merry 						].lun_number;
471130f4520SKenneth D. Merry 
472130f4520SKenneth D. Merry 					if (bit_test(ctx->lun_mask, lun) == 0)
473130f4520SKenneth D. Merry 						continue;
474130f4520SKenneth D. Merry 					fprintf(stdout, "%15.6s%d ",
475130f4520SKenneth D. Merry 						"lun", lun);
476130f4520SKenneth D. Merry 					hdr_devs++;
477130f4520SKenneth D. Merry 				}
478130f4520SKenneth D. Merry 				fprintf(stdout, "\n");
479130f4520SKenneth D. Merry 			}
480130f4520SKenneth D. Merry 			for (i = 0; i < hdr_devs; i++)
481130f4520SKenneth D. Merry 				fprintf(stdout, "%s  %sKB/t %s  MB/s ",
482130f4520SKenneth D. Merry 					((F_CPU(ctx) != 0) && (i == 0) &&
483130f4520SKenneth D. Merry 					(F_TOTALS(ctx) == 0)) ?  "       " : "",
484130f4520SKenneth D. Merry 					(F_LUNVAL(ctx) != 0) ? " ms  " : "",
485130f4520SKenneth D. Merry 					(F_DMA(ctx) == 0) ? "tps" : "dps");
486130f4520SKenneth D. Merry 			fprintf(stdout, "\n");
487130f4520SKenneth D. Merry 			ctx->header_interval = 20;
488130f4520SKenneth D. Merry 		}
489130f4520SKenneth D. Merry 	}
490130f4520SKenneth D. Merry 
491130f4520SKenneth D. Merry 	if (F_TOTALS(ctx) != 0) {
492130f4520SKenneth D. Merry 		long double mbsec[3];
493130f4520SKenneth D. Merry 		long double kb_per_transfer[3];
494130f4520SKenneth D. Merry 		long double transfers_per_sec[3];
495130f4520SKenneth D. Merry 		long double ms_per_transfer[3];
496130f4520SKenneth D. Merry 		long double ms_per_dma[3];
497130f4520SKenneth D. Merry 		long double dmas_per_sec[3];
498130f4520SKenneth D. Merry 
499130f4520SKenneth D. Merry 		for (i = 0; i < 3; i++)
500130f4520SKenneth D. Merry 			ctx->prev_total_stats[i] = ctx->cur_total_stats[i];
501130f4520SKenneth D. Merry 
502130f4520SKenneth D. Merry 		memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats));
503130f4520SKenneth D. Merry 
504130f4520SKenneth D. Merry 		/* Use macros to make the next loop more readable. */
505130f4520SKenneth D. Merry #define	ADD_STATS_BYTES(st, p, i, j) \
506130f4520SKenneth D. Merry 	ctx->cur_total_stats[st].ports[p].bytes[j] += \
507130f4520SKenneth D. Merry 	    ctx->cur_lun_stats[i].ports[p].bytes[j]
508130f4520SKenneth D. Merry #define	ADD_STATS_OPERATIONS(st, p, i, j) \
509130f4520SKenneth D. Merry 	ctx->cur_total_stats[st].ports[p].operations[j] += \
510130f4520SKenneth D. Merry 	    ctx->cur_lun_stats[i].ports[p].operations[j]
511130f4520SKenneth D. Merry #define	ADD_STATS_NUM_DMAS(st, p, i, j) \
512130f4520SKenneth D. Merry 	ctx->cur_total_stats[st].ports[p].num_dmas[j] += \
513130f4520SKenneth D. Merry 	    ctx->cur_lun_stats[i].ports[p].num_dmas[j]
514130f4520SKenneth D. Merry #define	ADD_STATS_TIME(st, p, i, j) \
515130f4520SKenneth D. Merry 	bintime_add(&ctx->cur_total_stats[st].ports[p].time[j], \
516130f4520SKenneth D. Merry 	    &ctx->cur_lun_stats[i].ports[p].time[j])
517130f4520SKenneth D. Merry #define	ADD_STATS_DMA_TIME(st, p, i, j) \
518130f4520SKenneth D. Merry 	bintime_add(&ctx->cur_total_stats[st].ports[p].dma_time[j], \
519130f4520SKenneth D. Merry 	    &ctx->cur_lun_stats[i].ports[p].dma_time[j])
520130f4520SKenneth D. Merry 
521130f4520SKenneth D. Merry 		for (i = 0; i < ctx->num_luns; i++) {
522130f4520SKenneth D. Merry 			for (port = 0; port < CTL_MAX_PORTS; port++) {
523130f4520SKenneth D. Merry 				for (j = 0; j < CTL_STATS_NUM_TYPES; j++) {
524130f4520SKenneth D. Merry 					ADD_STATS_BYTES(2, port, i, j);
525130f4520SKenneth D. Merry 					ADD_STATS_OPERATIONS(2, port, i, j);
526130f4520SKenneth D. Merry 					ADD_STATS_NUM_DMAS(2, port, i, j);
527130f4520SKenneth D. Merry 					ADD_STATS_TIME(2, port, i, j);
528130f4520SKenneth D. Merry 					ADD_STATS_DMA_TIME(2, port, i, j);
529130f4520SKenneth D. Merry 				}
530130f4520SKenneth D. Merry 				ADD_STATS_BYTES(0, port, i, CTL_STATS_READ);
531130f4520SKenneth D. Merry 				ADD_STATS_OPERATIONS(0, port, i,
532130f4520SKenneth D. Merry 				    CTL_STATS_READ);
533130f4520SKenneth D. Merry 				ADD_STATS_NUM_DMAS(0, port, i, CTL_STATS_READ);
534130f4520SKenneth D. Merry 				ADD_STATS_TIME(0, port, i, CTL_STATS_READ);
535130f4520SKenneth D. Merry 				ADD_STATS_DMA_TIME(0, port, i, CTL_STATS_READ);
536130f4520SKenneth D. Merry 
537130f4520SKenneth D. Merry 				ADD_STATS_BYTES(1, port, i, CTL_STATS_WRITE);
538130f4520SKenneth D. Merry 				ADD_STATS_OPERATIONS(1, port, i,
539130f4520SKenneth D. Merry 				    CTL_STATS_WRITE);
540130f4520SKenneth D. Merry 				ADD_STATS_NUM_DMAS(1, port, i, CTL_STATS_WRITE);
541130f4520SKenneth D. Merry 				ADD_STATS_TIME(1, port, i, CTL_STATS_WRITE);
542130f4520SKenneth D. Merry 				ADD_STATS_DMA_TIME(1, port, i, CTL_STATS_WRITE);
543130f4520SKenneth D. Merry 			}
544130f4520SKenneth D. Merry 		}
545130f4520SKenneth D. Merry 
546130f4520SKenneth D. Merry 		for (i = 0; i < 3; i++) {
547130f4520SKenneth D. Merry 			compute_stats(&ctx->cur_total_stats[i],
548130f4520SKenneth D. Merry 				F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i],
549130f4520SKenneth D. Merry 				etime, &mbsec[i], &kb_per_transfer[i],
550130f4520SKenneth D. Merry 				&transfers_per_sec[i],
551130f4520SKenneth D. Merry 				&ms_per_transfer[i], &ms_per_dma[i],
552130f4520SKenneth D. Merry 				&dmas_per_sec[i]);
553130f4520SKenneth D. Merry 			if (F_DMA(ctx) != 0)
554130f4520SKenneth D. Merry 				fprintf(stdout, " %2.2Lf",
555130f4520SKenneth D. Merry 					ms_per_dma[i]);
556130f4520SKenneth D. Merry 			else if (F_LUNVAL(ctx) != 0)
557130f4520SKenneth D. Merry 				fprintf(stdout, " %2.2Lf",
558130f4520SKenneth D. Merry 					ms_per_transfer[i]);
559130f4520SKenneth D. Merry 			fprintf(stdout, " %5.2Lf %3.0Lf %5.2Lf ",
560130f4520SKenneth D. Merry 				kb_per_transfer[i],
561130f4520SKenneth D. Merry 				(F_DMA(ctx) == 0) ? transfers_per_sec[i] :
562130f4520SKenneth D. Merry 				dmas_per_sec[i], mbsec[i]);
563130f4520SKenneth D. Merry 		}
564130f4520SKenneth D. Merry 		if (F_CPU(ctx))
565130f4520SKenneth D. Merry 			fprintf(stdout, " %5.1Lf%%", cpu_percentage);
566130f4520SKenneth D. Merry 	} else {
567130f4520SKenneth D. Merry 		if (F_CPU(ctx))
568130f4520SKenneth D. Merry 			fprintf(stdout, "%5.1Lf%% ", cpu_percentage);
569130f4520SKenneth D. Merry 
570130f4520SKenneth D. Merry 		for (i = 0; i < min(CTL_STAT_LUN_BITS, ctx->num_luns); i++) {
571130f4520SKenneth D. Merry 			long double mbsec, kb_per_transfer;
572130f4520SKenneth D. Merry 			long double transfers_per_sec;
573130f4520SKenneth D. Merry 			long double ms_per_transfer;
574130f4520SKenneth D. Merry 			long double ms_per_dma;
575130f4520SKenneth D. Merry 			long double dmas_per_sec;
576130f4520SKenneth D. Merry 
577130f4520SKenneth D. Merry 			if (bit_test(ctx->lun_mask,
578130f4520SKenneth D. Merry 			    (int)ctx->cur_lun_stats[i].lun_number) == 0)
579130f4520SKenneth D. Merry 				continue;
580130f4520SKenneth D. Merry 			compute_stats(&ctx->cur_lun_stats[i], F_FIRST(ctx) ?
581130f4520SKenneth D. Merry 				NULL : &ctx->prev_lun_stats[i], etime,
582130f4520SKenneth D. Merry 				&mbsec, &kb_per_transfer,
583130f4520SKenneth D. Merry 				&transfers_per_sec, &ms_per_transfer,
584130f4520SKenneth D. Merry 				&ms_per_dma, &dmas_per_sec);
585130f4520SKenneth D. Merry 			if (F_DMA(ctx))
586130f4520SKenneth D. Merry 				fprintf(stdout, " %2.2Lf",
587130f4520SKenneth D. Merry 					ms_per_dma);
588130f4520SKenneth D. Merry 			else if (F_LUNVAL(ctx) != 0)
589130f4520SKenneth D. Merry 				fprintf(stdout, " %2.2Lf",
590130f4520SKenneth D. Merry 					ms_per_transfer);
591130f4520SKenneth D. Merry 			fprintf(stdout, " %5.2Lf %3.0Lf %5.2Lf ",
592130f4520SKenneth D. Merry 				kb_per_transfer, (F_DMA(ctx) == 0) ?
593130f4520SKenneth D. Merry 				transfers_per_sec : dmas_per_sec, mbsec);
594130f4520SKenneth D. Merry 		}
595130f4520SKenneth D. Merry 	}
596130f4520SKenneth D. Merry }
597130f4520SKenneth D. Merry 
598130f4520SKenneth D. Merry int
599130f4520SKenneth D. Merry main(int argc, char **argv)
600130f4520SKenneth D. Merry {
601130f4520SKenneth D. Merry 	int c;
602130f4520SKenneth D. Merry 	int count, waittime;
603130f4520SKenneth D. Merry 	int set_lun;
604130f4520SKenneth D. Merry 	int fd, retval;
605130f4520SKenneth D. Merry 	struct ctlstat_context ctx;
606130f4520SKenneth D. Merry 
607130f4520SKenneth D. Merry 	/* default values */
608130f4520SKenneth D. Merry 	retval = 0;
609130f4520SKenneth D. Merry 	waittime = 1;
610130f4520SKenneth D. Merry 	count = -1;
611130f4520SKenneth D. Merry 	memset(&ctx, 0, sizeof(ctx));
612130f4520SKenneth D. Merry 	ctx.numdevs = 3;
613130f4520SKenneth D. Merry 	ctx.mode = CTLSTAT_MODE_STANDARD;
614130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_CPU;
615130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_FIRST_RUN;
616130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_HEADER;
617130f4520SKenneth D. Merry 
618130f4520SKenneth D. Merry 	while ((c = getopt(argc, argv, ctlstat_opts)) != -1) {
619130f4520SKenneth D. Merry 		switch (c) {
620130f4520SKenneth D. Merry 		case 'C':
621130f4520SKenneth D. Merry 			ctx.flags &= ~CTLSTAT_FLAG_CPU;
622130f4520SKenneth D. Merry 			break;
623130f4520SKenneth D. Merry 		case 'c':
624130f4520SKenneth D. Merry 			count = atoi(optarg);
625130f4520SKenneth D. Merry 			break;
626130f4520SKenneth D. Merry 		case 'd':
627130f4520SKenneth D. Merry 			ctx.flags |= CTLSTAT_FLAG_DMA_TIME;
628130f4520SKenneth D. Merry 			break;
629130f4520SKenneth D. Merry 		case 'D':
630130f4520SKenneth D. Merry 			ctx.mode = CTLSTAT_MODE_DUMP;
631130f4520SKenneth D. Merry 			waittime = 30;
632130f4520SKenneth D. Merry 			break;
633130f4520SKenneth D. Merry 		case 'h':
634130f4520SKenneth D. Merry 			ctx.flags &= ~CTLSTAT_FLAG_HEADER;
635130f4520SKenneth D. Merry 			break;
636130f4520SKenneth D. Merry 		case 'j':
637130f4520SKenneth D. Merry 			ctx.mode = CTLSTAT_MODE_JSON;
638130f4520SKenneth D. Merry 			waittime = 30;
639130f4520SKenneth D. Merry 			break;
640130f4520SKenneth D. Merry 		case 'l': {
641130f4520SKenneth D. Merry 			int cur_lun;
642130f4520SKenneth D. Merry 
643130f4520SKenneth D. Merry 			cur_lun = atoi(optarg);
644130f4520SKenneth D. Merry 			if (cur_lun > CTL_STAT_LUN_BITS)
645130f4520SKenneth D. Merry 				errx(1, "Invalid LUN number %d", cur_lun);
646130f4520SKenneth D. Merry 
647130f4520SKenneth D. Merry 			bit_ffs(ctx.lun_mask, CTL_STAT_LUN_BITS, &set_lun);
648130f4520SKenneth D. Merry 			if (set_lun == -1)
649130f4520SKenneth D. Merry 				ctx.numdevs = 1;
650130f4520SKenneth D. Merry 			else
651130f4520SKenneth D. Merry 				ctx.numdevs++;
652130f4520SKenneth D. Merry 			bit_set(ctx.lun_mask, cur_lun);
653130f4520SKenneth D. Merry 			break;
654130f4520SKenneth D. Merry 		}
655130f4520SKenneth D. Merry 		case 'n':
656130f4520SKenneth D. Merry 			ctx.numdevs = atoi(optarg);
657130f4520SKenneth D. Merry 			break;
658130f4520SKenneth D. Merry 		case 't':
659130f4520SKenneth D. Merry 			ctx.flags |= CTLSTAT_FLAG_TOTALS;
660130f4520SKenneth D. Merry 			ctx.numdevs = 3;
661130f4520SKenneth D. Merry 			break;
662130f4520SKenneth D. Merry 		case 'w':
663130f4520SKenneth D. Merry 			waittime = atoi(optarg);
664130f4520SKenneth D. Merry 			break;
665130f4520SKenneth D. Merry 		default:
666130f4520SKenneth D. Merry 			retval = 1;
667130f4520SKenneth D. Merry 			usage(retval);
668130f4520SKenneth D. Merry 			exit(retval);
669130f4520SKenneth D. Merry 			break;
670130f4520SKenneth D. Merry 		}
671130f4520SKenneth D. Merry 	}
672130f4520SKenneth D. Merry 
673130f4520SKenneth D. Merry 	bit_ffs(ctx.lun_mask, CTL_STAT_LUN_BITS, &set_lun);
674130f4520SKenneth D. Merry 
675130f4520SKenneth D. Merry 	if ((F_TOTALS(&ctx))
676130f4520SKenneth D. Merry 	 && (set_lun != -1)) {
677130f4520SKenneth D. Merry 		errx(1, "Total Mode (-t) is incompatible with individual "
678130f4520SKenneth D. Merry 		     "LUN mode (-l)");
679130f4520SKenneth D. Merry 	} else if (set_lun == -1) {
680130f4520SKenneth D. Merry 		/*
681130f4520SKenneth D. Merry 		 * Note that this just selects the first N LUNs to display,
682130f4520SKenneth D. Merry 		 * but at this point we have no knoweledge of which LUN
683130f4520SKenneth D. Merry 		 * numbers actually exist.  So we may select LUNs that
684130f4520SKenneth D. Merry 		 * aren't there.
685130f4520SKenneth D. Merry 		 */
686130f4520SKenneth D. Merry 		bit_nset(ctx.lun_mask, 0, min(ctx.numdevs - 1,
687130f4520SKenneth D. Merry 			 CTL_STAT_LUN_BITS - 1));
688130f4520SKenneth D. Merry 	}
689130f4520SKenneth D. Merry 
690130f4520SKenneth D. Merry 	if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1)
691130f4520SKenneth D. Merry 		err(1, "cannot open %s", CTL_DEFAULT_DEV);
692130f4520SKenneth D. Merry 
693130f4520SKenneth D. Merry 	for (;count != 0;) {
694130f4520SKenneth D. Merry 		ctx.tmp_lun_stats = ctx.prev_lun_stats;
695130f4520SKenneth D. Merry 		ctx.prev_lun_stats = ctx.cur_lun_stats;
696130f4520SKenneth D. Merry 		ctx.cur_lun_stats = ctx.tmp_lun_stats;
697130f4520SKenneth D. Merry 		ctx.prev_time = ctx.cur_time;
698130f4520SKenneth D. Merry 		ctx.prev_cpu = ctx.cur_cpu;
699130f4520SKenneth D. Merry 		if (getstats(fd, &ctx.num_luns, &ctx.cur_lun_stats,
700130f4520SKenneth D. Merry 			     &ctx.cur_time, &ctx.flags) != 0)
701130f4520SKenneth D. Merry 			errx(1, "error returned from getstats()");
702130f4520SKenneth D. Merry 
703130f4520SKenneth D. Merry 		switch(ctx.mode) {
704130f4520SKenneth D. Merry 		case CTLSTAT_MODE_STANDARD:
705130f4520SKenneth D. Merry 			ctlstat_standard(&ctx);
706130f4520SKenneth D. Merry 			break;
707130f4520SKenneth D. Merry 		case CTLSTAT_MODE_DUMP:
708130f4520SKenneth D. Merry 			ctlstat_dump(&ctx);
709130f4520SKenneth D. Merry 			break;
710130f4520SKenneth D. Merry 		case CTLSTAT_MODE_JSON:
711130f4520SKenneth D. Merry 			ctlstat_json(&ctx);
712130f4520SKenneth D. Merry 			break;
713130f4520SKenneth D. Merry 		default:
714130f4520SKenneth D. Merry 			break;
715130f4520SKenneth D. Merry 		}
716130f4520SKenneth D. Merry 
717130f4520SKenneth D. Merry 		fprintf(stdout, "\n");
718130f4520SKenneth D. Merry 		ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN;
719130f4520SKenneth D. Merry 		if (count != 1)
720130f4520SKenneth D. Merry 			sleep(waittime);
721130f4520SKenneth D. Merry 		if (count > 0)
722130f4520SKenneth D. Merry 			count--;
723130f4520SKenneth D. Merry 	}
724130f4520SKenneth D. Merry 
725130f4520SKenneth D. Merry 	exit (retval);
726130f4520SKenneth D. Merry }
727130f4520SKenneth D. Merry 
728130f4520SKenneth D. Merry /*
729130f4520SKenneth D. Merry  * vim: ts=8
730130f4520SKenneth D. Merry  */
731