ctlstat.c revision 288784
1/*-
2 * Copyright (c) 2004, 2008, 2009 Silicon Graphics International Corp.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions, and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    substantially similar to the "NO WARRANTY" disclaimer below
13 *    ("Disclaimer") and any redistribution must be conditioned upon
14 *    including a substantially similar Disclaimer requirement for further
15 *    binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGES.
29 *
30 * $Id: //depot/users/kenm/FreeBSD-test2/usr.bin/ctlstat/ctlstat.c#4 $
31 */
32/*
33 * CAM Target Layer statistics program
34 *
35 * Authors: Ken Merry <ken@FreeBSD.org>, Will Andrews <will@FreeBSD.org>
36 */
37
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: stable/10/usr.bin/ctlstat/ctlstat.c 288784 2015-10-05 10:49:01Z mav $");
40
41#include <sys/ioctl.h>
42#include <sys/types.h>
43#include <sys/param.h>
44#include <sys/time.h>
45#include <sys/sysctl.h>
46#include <sys/resource.h>
47#include <sys/queue.h>
48#include <sys/callout.h>
49#include <stdint.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <unistd.h>
53#include <fcntl.h>
54#include <getopt.h>
55#include <string.h>
56#include <errno.h>
57#include <err.h>
58#include <ctype.h>
59#include <bitstring.h>
60#include <cam/scsi/scsi_all.h>
61#include <cam/ctl/ctl.h>
62#include <cam/ctl/ctl_io.h>
63#include <cam/ctl/ctl_scsi_all.h>
64#include <cam/ctl/ctl_util.h>
65#include <cam/ctl/ctl_backend.h>
66#include <cam/ctl/ctl_ioctl.h>
67
68/*
69 * The default amount of space we allocate for LUN storage space.  We
70 * dynamically allocate more if needed.
71 */
72#define	CTL_STAT_NUM_LUNS	30
73
74/*
75 * The default number of LUN selection bits we allocate.  This is large
76 * because we don't currently increase it if the user specifies a LUN
77 * number of 1024 or larger.
78 */
79#define	CTL_STAT_LUN_BITS	1024L
80
81static const char *ctlstat_opts = "Cc:Ddhjl:n:p:tw:";
82static const char *ctlstat_usage = "Usage:  ctlstat [-CDdjht] [-l lunnum]"
83				   "[-c count] [-n numdevs] [-w wait]\n";
84
85struct ctl_cpu_stats {
86	uint64_t user;
87	uint64_t nice;
88	uint64_t system;
89	uint64_t intr;
90	uint64_t idle;
91};
92
93typedef enum {
94	CTLSTAT_MODE_STANDARD,
95	CTLSTAT_MODE_DUMP,
96	CTLSTAT_MODE_JSON,
97} ctlstat_mode_types;
98
99#define	CTLSTAT_FLAG_CPU		(1 << 0)
100#define	CTLSTAT_FLAG_HEADER		(1 << 1)
101#define	CTLSTAT_FLAG_FIRST_RUN		(1 << 2)
102#define	CTLSTAT_FLAG_TOTALS		(1 << 3)
103#define	CTLSTAT_FLAG_DMA_TIME		(1 << 4)
104#define	CTLSTAT_FLAG_LUN_TIME_VALID	(1 << 5)
105#define	CTLSTAT_FLAG_LUN_MASK		(1 << 6)
106#define	CTLSTAT_FLAG_PORT_MASK		(1 << 7)
107#define	F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU)
108#define	F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER)
109#define	F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN)
110#define	F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS)
111#define	F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME)
112#define	F_LUNVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUN_TIME_VALID)
113#define	F_LUNMASK(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUN_MASK)
114#define	F_PORTMASK(ctx) ((ctx)->flags & CTLSTAT_FLAG_PORT_MASK)
115
116struct ctlstat_context {
117	ctlstat_mode_types mode;
118	int flags;
119	struct ctl_lun_io_stats *cur_lun_stats, *prev_lun_stats,
120		*tmp_lun_stats;
121	struct ctl_lun_io_stats cur_total_stats[3], prev_total_stats[3];
122	struct timespec cur_time, prev_time;
123	struct ctl_cpu_stats cur_cpu, prev_cpu;
124	uint64_t cur_total_jiffies, prev_total_jiffies;
125	uint64_t cur_idle, prev_idle;
126	bitstr_t bit_decl(lun_mask, CTL_STAT_LUN_BITS);
127	bitstr_t bit_decl(port_mask, CTL_MAX_PORTS);
128	int num_luns;
129	int numdevs;
130	int header_interval;
131};
132
133#ifndef min
134#define	min(x,y)	(((x) < (y)) ? (x) : (y))
135#endif
136
137static void usage(int error);
138static int getstats(int fd, int *num_luns, struct ctl_lun_io_stats **xlun_stats,
139		    struct timespec *cur_time, int *lun_time_valid);
140static int getcpu(struct ctl_cpu_stats *cpu_stats);
141static void compute_stats(struct ctlstat_context *ctx,
142			  struct ctl_lun_io_stats *cur_stats,
143			  struct ctl_lun_io_stats *prev_stats,
144			  long double etime, long double *mbsec,
145			  long double *kb_per_transfer,
146			  long double *transfers_per_second,
147			  long double *ms_per_transfer,
148			  long double *ms_per_dma,
149			  long double *dmas_per_second);
150
151static void
152usage(int error)
153{
154	fputs(ctlstat_usage, error ? stderr : stdout);
155}
156
157static int
158getstats(int fd, int *num_luns, struct ctl_lun_io_stats **xlun_stats,
159	 struct timespec *cur_time, int *flags)
160{
161	struct ctl_lun_io_stats *lun_stats;
162	struct ctl_stats stats;
163	int more_space_count;
164
165	more_space_count = 0;
166
167	if (*num_luns == 0)
168		*num_luns = CTL_STAT_NUM_LUNS;
169
170	lun_stats = *xlun_stats;
171retry:
172
173	if (lun_stats == NULL) {
174		lun_stats = (struct ctl_lun_io_stats *)malloc(
175			sizeof(*lun_stats) * *num_luns);
176	}
177
178	memset(&stats, 0, sizeof(stats));
179	stats.alloc_len = *num_luns * sizeof(*lun_stats);
180	memset(lun_stats, 0, stats.alloc_len);
181	stats.lun_stats = lun_stats;
182
183	if (ioctl(fd, CTL_GETSTATS, &stats) == -1)
184		err(1, "error returned from CTL_GETSTATS ioctl");
185
186	switch (stats.status) {
187	case CTL_SS_OK:
188		break;
189	case CTL_SS_ERROR:
190		err(1, "CTL_SS_ERROR returned from CTL_GETSTATS ioctl");
191		break;
192	case CTL_SS_NEED_MORE_SPACE:
193		if (more_space_count > 0) {
194			errx(1, "CTL_GETSTATS returned NEED_MORE_SPACE again");
195		}
196		*num_luns = stats.num_luns;
197		free(lun_stats);
198		lun_stats = NULL;
199		more_space_count++;
200		goto retry;
201		break; /* NOTREACHED */
202	default:
203		errx(1, "unknown status %d returned from CTL_GETSTATS ioctl",
204		     stats.status);
205		break;
206	}
207
208	*xlun_stats = lun_stats;
209	*num_luns = stats.num_luns;
210	cur_time->tv_sec = stats.timestamp.tv_sec;
211	cur_time->tv_nsec = stats.timestamp.tv_nsec;
212	if (stats.flags & CTL_STATS_FLAG_TIME_VALID)
213		*flags |= CTLSTAT_FLAG_LUN_TIME_VALID;
214	else
215		*flags &= ~CTLSTAT_FLAG_LUN_TIME_VALID;
216
217	return (0);
218}
219
220static int
221getcpu(struct ctl_cpu_stats *cpu_stats)
222{
223	long cp_time[CPUSTATES];
224	size_t cplen;
225
226	cplen = sizeof(cp_time);
227
228	if (sysctlbyname("kern.cp_time", &cp_time, &cplen, NULL, 0) == -1) {
229		warn("sysctlbyname(kern.cp_time...) failed");
230		return (1);
231	}
232
233	cpu_stats->user = cp_time[CP_USER];
234	cpu_stats->nice = cp_time[CP_NICE];
235	cpu_stats->system = cp_time[CP_SYS];
236	cpu_stats->intr = cp_time[CP_INTR];
237	cpu_stats->idle = cp_time[CP_IDLE];
238
239	return (0);
240}
241
242static void
243compute_stats(struct ctlstat_context *ctx, struct ctl_lun_io_stats *cur_stats,
244	      struct ctl_lun_io_stats *prev_stats, long double etime,
245	      long double *mbsec, long double *kb_per_transfer,
246	      long double *transfers_per_second, long double *ms_per_transfer,
247	      long double *ms_per_dma, long double *dmas_per_second)
248{
249	uint64_t total_bytes = 0, total_operations = 0, total_dmas = 0;
250	uint32_t port;
251	struct bintime total_time_bt, total_dma_bt;
252	struct timespec total_time_ts, total_dma_ts;
253	int i;
254
255	bzero(&total_time_bt, sizeof(total_time_bt));
256	bzero(&total_dma_bt, sizeof(total_dma_bt));
257	bzero(&total_time_ts, sizeof(total_time_ts));
258	bzero(&total_dma_ts, sizeof(total_dma_ts));
259	for (port = 0; port < CTL_MAX_PORTS; port++) {
260		if (F_PORTMASK(ctx) &&
261		    bit_test(ctx->port_mask, port) == 0)
262			continue;
263		for (i = 0; i < CTL_STATS_NUM_TYPES; i++) {
264			total_bytes += cur_stats->ports[port].bytes[i];
265			total_operations +=
266			    cur_stats->ports[port].operations[i];
267			total_dmas += cur_stats->ports[port].num_dmas[i];
268			bintime_add(&total_time_bt,
269			    &cur_stats->ports[port].time[i]);
270			bintime_add(&total_dma_bt,
271			    &cur_stats->ports[port].dma_time[i]);
272			if (prev_stats != NULL) {
273				total_bytes -=
274				    prev_stats->ports[port].bytes[i];
275				total_operations -=
276				    prev_stats->ports[port].operations[i];
277				total_dmas -=
278				    prev_stats->ports[port].num_dmas[i];
279				bintime_sub(&total_time_bt,
280				    &prev_stats->ports[port].time[i]);
281				bintime_sub(&total_dma_bt,
282				    &prev_stats->ports[port].dma_time[i]);
283			}
284		}
285	}
286
287	*mbsec = total_bytes;
288	*mbsec /= 1024 * 1024;
289	if (etime > 0.0)
290		*mbsec /= etime;
291	else
292		*mbsec = 0;
293	*kb_per_transfer = total_bytes;
294	*kb_per_transfer /= 1024;
295	if (total_operations > 0)
296		*kb_per_transfer /= total_operations;
297	else
298		*kb_per_transfer = 0;
299	*transfers_per_second = total_operations;
300	*dmas_per_second = total_dmas;
301	if (etime > 0.0) {
302		*transfers_per_second /= etime;
303		*dmas_per_second /= etime;
304	} else {
305		*transfers_per_second = 0;
306		*dmas_per_second = 0;
307	}
308
309	bintime2timespec(&total_time_bt, &total_time_ts);
310	bintime2timespec(&total_dma_bt, &total_dma_ts);
311	if (total_operations > 0) {
312		/*
313		 * Convert the timespec to milliseconds.
314		 */
315		*ms_per_transfer = total_time_ts.tv_sec * 1000;
316		*ms_per_transfer += total_time_ts.tv_nsec / 1000000;
317		*ms_per_transfer /= total_operations;
318	} else
319		*ms_per_transfer = 0;
320
321	if (total_dmas > 0) {
322		/*
323		 * Convert the timespec to milliseconds.
324		 */
325		*ms_per_dma = total_dma_ts.tv_sec * 1000;
326		*ms_per_dma += total_dma_ts.tv_nsec / 1000000;
327		*ms_per_dma /= total_dmas;
328	} else
329		*ms_per_dma = 0;
330}
331
332/* The dump_stats() and json_stats() functions perform essentially the same
333 * purpose, but dump the statistics in different formats.  JSON is more
334 * conducive to programming, however.
335 */
336
337#define	PRINT_BINTIME(prefix, bt) \
338	printf("%s %jd.%06ju\n", prefix, (intmax_t)(bt).sec, \
339	       (uintmax_t)(((bt).frac >> 32) * 1000000 >> 32))
340static const char *iotypes[] = {"NO IO", "READ", "WRITE"};
341
342static void
343ctlstat_dump(struct ctlstat_context *ctx) {
344	int iotype, lun, port;
345	struct ctl_lun_io_stats *stats = ctx->cur_lun_stats;
346
347	for (lun = 0; lun < ctx->num_luns;lun++) {
348		if (F_LUNMASK(ctx) && bit_test(ctx->lun_mask, lun) == 0)
349			continue;
350		printf("lun %d\n", lun);
351		for (port = 0; port < CTL_MAX_PORTS; port++) {
352			if (F_PORTMASK(ctx) &&
353			    bit_test(ctx->port_mask, port) == 0)
354				continue;
355			printf(" port %d\n",
356			    stats[lun].ports[port].targ_port);
357			for (iotype = 0; iotype < CTL_STATS_NUM_TYPES;
358			    iotype++) {
359				printf("  io type %d (%s)\n", iotype,
360				    iotypes[iotype]);
361				printf("   bytes %ju\n", (uintmax_t)
362				    stats[lun].ports[port].bytes[iotype]);
363				printf("   operations %ju\n", (uintmax_t)
364				    stats[lun].ports[port].operations[iotype]);
365				PRINT_BINTIME("   io time",
366				    stats[lun].ports[port].time[iotype]);
367				printf("   num dmas %ju\n", (uintmax_t)
368				    stats[lun].ports[port].num_dmas[iotype]);
369				PRINT_BINTIME("   dma time",
370				    stats[lun].ports[port].dma_time[iotype]);
371			}
372		}
373	}
374}
375
376#define	JSON_BINTIME(prefix, bt) \
377	printf("\"%s\":%jd.%06ju,", prefix, (intmax_t)(bt).sec, \
378	    (uintmax_t)(((bt).frac >> 32) * 1000000 >> 32))
379static void
380ctlstat_json(struct ctlstat_context *ctx) {
381	int iotype, lun, port;
382	struct ctl_lun_io_stats *stats = ctx->cur_lun_stats;
383
384	printf("{\"luns\":[");
385	for (lun = 0; lun < ctx->num_luns; lun++) {
386		if (F_LUNMASK(ctx) && bit_test(ctx->lun_mask, lun) == 0)
387			continue;
388		printf("{\"ports\":[");
389		for (port = 0; port < CTL_MAX_PORTS;port++) {
390			if (F_PORTMASK(ctx) &&
391			    bit_test(ctx->port_mask, port) == 0)
392				continue;
393			printf("{\"num\":%d,\"io\":[",
394			    stats[lun].ports[port].targ_port);
395			for (iotype = 0; iotype < CTL_STATS_NUM_TYPES;
396			    iotype++) {
397				printf("{\"type\":\"%s\",", iotypes[iotype]);
398				printf("\"bytes\":%ju,", (uintmax_t)stats[
399				       lun].ports[port].bytes[iotype]);
400				printf("\"operations\":%ju,", (uintmax_t)stats[
401				       lun].ports[port].operations[iotype]);
402				JSON_BINTIME("io time",
403				    stats[lun].ports[port].time[iotype]);
404				JSON_BINTIME("dma time",
405				    stats[lun].ports[port].dma_time[iotype]);
406				printf("\"num dmas\":%ju}", (uintmax_t)
407				    stats[lun].ports[port].num_dmas[iotype]);
408				if (iotype < (CTL_STATS_NUM_TYPES - 1))
409					printf(","); /* continue io array */
410			}
411			printf("]}"); /* close port */
412			if (port < (CTL_MAX_PORTS - 1))
413				printf(","); /* continue port array */
414		}
415		printf("]}"); /* close lun */
416		if (lun < (ctx->num_luns - 1))
417			printf(","); /* continue lun array */
418	}
419	printf("]}"); /* close luns and toplevel */
420}
421
422static void
423ctlstat_standard(struct ctlstat_context *ctx) {
424	long double etime;
425	uint64_t delta_jiffies, delta_idle;
426	uint32_t port;
427	long double cpu_percentage;
428	int i;
429	int j;
430
431	cpu_percentage = 0;
432
433	if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0))
434		errx(1, "error returned from getcpu()");
435
436	etime = ctx->cur_time.tv_sec - ctx->prev_time.tv_sec +
437	    (ctx->prev_time.tv_nsec - ctx->cur_time.tv_nsec) * 1e-9;
438
439	if (F_CPU(ctx)) {
440		ctx->prev_total_jiffies = ctx->cur_total_jiffies;
441		ctx->cur_total_jiffies = ctx->cur_cpu.user +
442		    ctx->cur_cpu.nice + ctx->cur_cpu.system +
443		    ctx->cur_cpu.intr + ctx->cur_cpu.idle;
444		delta_jiffies = ctx->cur_total_jiffies;
445		if (F_FIRST(ctx) == 0)
446			delta_jiffies -= ctx->prev_total_jiffies;
447		ctx->prev_idle = ctx->cur_idle;
448		ctx->cur_idle = ctx->cur_cpu.idle;
449		delta_idle = ctx->cur_idle - ctx->prev_idle;
450
451		cpu_percentage = delta_jiffies - delta_idle;
452		cpu_percentage /= delta_jiffies;
453		cpu_percentage *= 100;
454	}
455
456	if (F_HDR(ctx)) {
457		ctx->header_interval--;
458		if (ctx->header_interval <= 0) {
459			int hdr_devs;
460
461			hdr_devs = 0;
462
463			if (F_CPU(ctx))
464				fprintf(stdout, " CPU");
465			if (F_TOTALS(ctx)) {
466				fprintf(stdout, "%s     Read       %s"
467					"    Write       %s    Total\n",
468					(F_LUNVAL(ctx) != 0) ? "      " : "",
469					(F_LUNVAL(ctx) != 0) ? "      " : "",
470					(F_LUNVAL(ctx) != 0) ? "      " : "");
471				hdr_devs = 3;
472			} else {
473				for (i = 0; i < min(CTL_STAT_LUN_BITS,
474				     ctx->num_luns); i++) {
475					int lun;
476
477					/*
478					 * Obviously this won't work with
479					 * LUN numbers greater than a signed
480					 * integer.
481					 */
482					lun = (int)ctx->cur_lun_stats[i
483						].lun_number;
484
485					if (F_LUNMASK(ctx) &&
486					    bit_test(ctx->lun_mask, lun) == 0)
487						continue;
488					fprintf(stdout, "%15.6s%d %s",
489					    "lun", lun,
490					    (F_LUNVAL(ctx) != 0) ? "     " : "");
491					hdr_devs++;
492				}
493				fprintf(stdout, "\n");
494			}
495			if (F_CPU(ctx))
496				fprintf(stdout, "    ");
497			for (i = 0; i < hdr_devs; i++)
498				fprintf(stdout, "%s KB/t   %s MB/s",
499					(F_LUNVAL(ctx) != 0) ? "    ms" : "",
500					(F_DMA(ctx) == 0) ? "tps" : "dps");
501			fprintf(stdout, "\n");
502			ctx->header_interval = 20;
503		}
504	}
505
506	if (F_CPU(ctx))
507		fprintf(stdout, "%3.0Lf%%", cpu_percentage);
508	if (F_TOTALS(ctx) != 0) {
509		long double mbsec[3];
510		long double kb_per_transfer[3];
511		long double transfers_per_sec[3];
512		long double ms_per_transfer[3];
513		long double ms_per_dma[3];
514		long double dmas_per_sec[3];
515
516		for (i = 0; i < 3; i++)
517			ctx->prev_total_stats[i] = ctx->cur_total_stats[i];
518
519		memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats));
520
521		/* Use macros to make the next loop more readable. */
522#define	ADD_STATS_BYTES(st, p, i, j) \
523	ctx->cur_total_stats[st].ports[p].bytes[j] += \
524	    ctx->cur_lun_stats[i].ports[p].bytes[j]
525#define	ADD_STATS_OPERATIONS(st, p, i, j) \
526	ctx->cur_total_stats[st].ports[p].operations[j] += \
527	    ctx->cur_lun_stats[i].ports[p].operations[j]
528#define	ADD_STATS_NUM_DMAS(st, p, i, j) \
529	ctx->cur_total_stats[st].ports[p].num_dmas[j] += \
530	    ctx->cur_lun_stats[i].ports[p].num_dmas[j]
531#define	ADD_STATS_TIME(st, p, i, j) \
532	bintime_add(&ctx->cur_total_stats[st].ports[p].time[j], \
533	    &ctx->cur_lun_stats[i].ports[p].time[j])
534#define	ADD_STATS_DMA_TIME(st, p, i, j) \
535	bintime_add(&ctx->cur_total_stats[st].ports[p].dma_time[j], \
536	    &ctx->cur_lun_stats[i].ports[p].dma_time[j])
537
538		for (i = 0; i < ctx->num_luns; i++) {
539			if (F_LUNMASK(ctx) && bit_test(ctx->lun_mask,
540			    (int)ctx->cur_lun_stats[i].lun_number) == 0)
541				continue;
542			for (port = 0; port < CTL_MAX_PORTS; port++) {
543				if (F_PORTMASK(ctx) &&
544				    bit_test(ctx->port_mask, port) == 0)
545					continue;
546				for (j = 0; j < CTL_STATS_NUM_TYPES; j++) {
547					ADD_STATS_BYTES(2, port, i, j);
548					ADD_STATS_OPERATIONS(2, port, i, j);
549					ADD_STATS_NUM_DMAS(2, port, i, j);
550					ADD_STATS_TIME(2, port, i, j);
551					ADD_STATS_DMA_TIME(2, port, i, j);
552				}
553				ADD_STATS_BYTES(0, port, i, CTL_STATS_READ);
554				ADD_STATS_OPERATIONS(0, port, i,
555				    CTL_STATS_READ);
556				ADD_STATS_NUM_DMAS(0, port, i, CTL_STATS_READ);
557				ADD_STATS_TIME(0, port, i, CTL_STATS_READ);
558				ADD_STATS_DMA_TIME(0, port, i, CTL_STATS_READ);
559
560				ADD_STATS_BYTES(1, port, i, CTL_STATS_WRITE);
561				ADD_STATS_OPERATIONS(1, port, i,
562				    CTL_STATS_WRITE);
563				ADD_STATS_NUM_DMAS(1, port, i, CTL_STATS_WRITE);
564				ADD_STATS_TIME(1, port, i, CTL_STATS_WRITE);
565				ADD_STATS_DMA_TIME(1, port, i, CTL_STATS_WRITE);
566			}
567		}
568
569		for (i = 0; i < 3; i++) {
570			compute_stats(ctx, &ctx->cur_total_stats[i],
571				F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i],
572				etime, &mbsec[i], &kb_per_transfer[i],
573				&transfers_per_sec[i],
574				&ms_per_transfer[i], &ms_per_dma[i],
575				&dmas_per_sec[i]);
576			if (F_DMA(ctx) != 0)
577				fprintf(stdout, " %5.1Lf",
578					ms_per_dma[i]);
579			else if (F_LUNVAL(ctx) != 0)
580				fprintf(stdout, " %5.1Lf",
581					ms_per_transfer[i]);
582			fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf",
583				kb_per_transfer[i],
584				(F_DMA(ctx) == 0) ? transfers_per_sec[i] :
585				dmas_per_sec[i], mbsec[i]);
586		}
587	} else {
588		for (i = 0; i < min(CTL_STAT_LUN_BITS, ctx->num_luns); i++) {
589			long double mbsec, kb_per_transfer;
590			long double transfers_per_sec;
591			long double ms_per_transfer;
592			long double ms_per_dma;
593			long double dmas_per_sec;
594
595			if (F_LUNMASK(ctx) && bit_test(ctx->lun_mask,
596			    (int)ctx->cur_lun_stats[i].lun_number) == 0)
597				continue;
598			compute_stats(ctx, &ctx->cur_lun_stats[i],
599			    F_FIRST(ctx) ? NULL : &ctx->prev_lun_stats[i],
600			    etime, &mbsec, &kb_per_transfer,
601			    &transfers_per_sec, &ms_per_transfer,
602			    &ms_per_dma, &dmas_per_sec);
603			if (F_DMA(ctx))
604				fprintf(stdout, " %5.1Lf",
605					ms_per_dma);
606			else if (F_LUNVAL(ctx) != 0)
607				fprintf(stdout, " %5.1Lf",
608					ms_per_transfer);
609			fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf",
610				kb_per_transfer, (F_DMA(ctx) == 0) ?
611				transfers_per_sec : dmas_per_sec, mbsec);
612		}
613	}
614}
615
616int
617main(int argc, char **argv)
618{
619	int c;
620	int count, waittime;
621	int fd, retval;
622	struct ctlstat_context ctx;
623
624	/* default values */
625	retval = 0;
626	waittime = 1;
627	count = -1;
628	memset(&ctx, 0, sizeof(ctx));
629	ctx.numdevs = 3;
630	ctx.mode = CTLSTAT_MODE_STANDARD;
631	ctx.flags |= CTLSTAT_FLAG_CPU;
632	ctx.flags |= CTLSTAT_FLAG_FIRST_RUN;
633	ctx.flags |= CTLSTAT_FLAG_HEADER;
634
635	while ((c = getopt(argc, argv, ctlstat_opts)) != -1) {
636		switch (c) {
637		case 'C':
638			ctx.flags &= ~CTLSTAT_FLAG_CPU;
639			break;
640		case 'c':
641			count = atoi(optarg);
642			break;
643		case 'd':
644			ctx.flags |= CTLSTAT_FLAG_DMA_TIME;
645			break;
646		case 'D':
647			ctx.mode = CTLSTAT_MODE_DUMP;
648			waittime = 30;
649			break;
650		case 'h':
651			ctx.flags &= ~CTLSTAT_FLAG_HEADER;
652			break;
653		case 'j':
654			ctx.mode = CTLSTAT_MODE_JSON;
655			waittime = 30;
656			break;
657		case 'l': {
658			int cur_lun;
659
660			cur_lun = atoi(optarg);
661			if (cur_lun > CTL_STAT_LUN_BITS)
662				errx(1, "Invalid LUN number %d", cur_lun);
663
664			if (!F_LUNMASK(&ctx))
665				ctx.numdevs = 1;
666			else
667				ctx.numdevs++;
668			bit_set(ctx.lun_mask, cur_lun);
669			ctx.flags |= CTLSTAT_FLAG_LUN_MASK;
670			break;
671		}
672		case 'n':
673			ctx.numdevs = atoi(optarg);
674			break;
675		case 'p': {
676			int cur_port;
677
678			cur_port = atoi(optarg);
679			if (cur_port > CTL_MAX_PORTS)
680				errx(1, "Invalid LUN number %d", cur_port);
681
682			bit_set(ctx.port_mask, cur_port);
683			ctx.flags |= CTLSTAT_FLAG_PORT_MASK;
684			break;
685		}
686		case 't':
687			ctx.flags |= CTLSTAT_FLAG_TOTALS;
688			break;
689		case 'w':
690			waittime = atoi(optarg);
691			break;
692		default:
693			retval = 1;
694			usage(retval);
695			exit(retval);
696			break;
697		}
698	}
699
700	if (!F_TOTALS(&ctx) && !F_LUNMASK(&ctx)) {
701		/*
702		 * Note that this just selects the first N LUNs to display,
703		 * but at this point we have no knoweledge of which LUN
704		 * numbers actually exist.  So we may select LUNs that
705		 * aren't there.
706		 */
707		bit_nset(ctx.lun_mask, 0, min(ctx.numdevs - 1,
708			 CTL_STAT_LUN_BITS - 1));
709		ctx.flags |= CTLSTAT_FLAG_LUN_MASK;
710	}
711
712	if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1)
713		err(1, "cannot open %s", CTL_DEFAULT_DEV);
714
715	for (;count != 0;) {
716		ctx.tmp_lun_stats = ctx.prev_lun_stats;
717		ctx.prev_lun_stats = ctx.cur_lun_stats;
718		ctx.cur_lun_stats = ctx.tmp_lun_stats;
719		ctx.prev_time = ctx.cur_time;
720		ctx.prev_cpu = ctx.cur_cpu;
721		if (getstats(fd, &ctx.num_luns, &ctx.cur_lun_stats,
722			     &ctx.cur_time, &ctx.flags) != 0)
723			errx(1, "error returned from getstats()");
724
725		switch(ctx.mode) {
726		case CTLSTAT_MODE_STANDARD:
727			ctlstat_standard(&ctx);
728			break;
729		case CTLSTAT_MODE_DUMP:
730			ctlstat_dump(&ctx);
731			break;
732		case CTLSTAT_MODE_JSON:
733			ctlstat_json(&ctx);
734			break;
735		default:
736			break;
737		}
738
739		fprintf(stdout, "\n");
740		ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN;
741		if (count != 1)
742			sleep(waittime);
743		if (count > 0)
744			count--;
745	}
746
747	exit (retval);
748}
749
750/*
751 * vim: ts=8
752 */
753