1229997Sken/*-
2229997Sken * Copyright (c) 2004, 2008, 2009 Silicon Graphics International Corp.
3312841Smav * Copyright (c) 2017 Alexander Motin <mav@FreeBSD.org>
4229997Sken * All rights reserved.
5229997Sken *
6229997Sken * Redistribution and use in source and binary forms, with or without
7229997Sken * modification, are permitted provided that the following conditions
8229997Sken * are met:
9229997Sken * 1. Redistributions of source code must retain the above copyright
10229997Sken *    notice, this list of conditions, and the following disclaimer,
11229997Sken *    without modification.
12229997Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13229997Sken *    substantially similar to the "NO WARRANTY" disclaimer below
14229997Sken *    ("Disclaimer") and any redistribution must be conditioned upon
15229997Sken *    including a substantially similar Disclaimer requirement for further
16229997Sken *    binary redistribution.
17229997Sken *
18229997Sken * NO WARRANTY
19229997Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20229997Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21229997Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
22229997Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23229997Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24229997Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25229997Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26229997Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27229997Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
28229997Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29229997Sken * POSSIBILITY OF SUCH DAMAGES.
30229997Sken *
31229997Sken * $Id: //depot/users/kenm/FreeBSD-test2/usr.bin/ctlstat/ctlstat.c#4 $
32229997Sken */
33229997Sken/*
34229997Sken * CAM Target Layer statistics program
35229997Sken *
36229997Sken * Authors: Ken Merry <ken@FreeBSD.org>, Will Andrews <will@FreeBSD.org>
37229997Sken */
38229997Sken
39229997Sken#include <sys/cdefs.h>
40229997Sken__FBSDID("$FreeBSD: stable/10/usr.bin/ctlstat/ctlstat.c 314992 2017-03-10 06:18:27Z mav $");
41229997Sken
42229997Sken#include <sys/ioctl.h>
43229997Sken#include <sys/types.h>
44229997Sken#include <sys/param.h>
45229997Sken#include <sys/time.h>
46229997Sken#include <sys/sysctl.h>
47229997Sken#include <sys/resource.h>
48229997Sken#include <sys/queue.h>
49229997Sken#include <sys/callout.h>
50229997Sken#include <stdint.h>
51229997Sken#include <stdio.h>
52229997Sken#include <stdlib.h>
53229997Sken#include <unistd.h>
54229997Sken#include <fcntl.h>
55229997Sken#include <getopt.h>
56229997Sken#include <string.h>
57229997Sken#include <errno.h>
58229997Sken#include <err.h>
59229997Sken#include <ctype.h>
60229997Sken#include <bitstring.h>
61229997Sken#include <cam/scsi/scsi_all.h>
62229997Sken#include <cam/ctl/ctl.h>
63229997Sken#include <cam/ctl/ctl_io.h>
64229997Sken#include <cam/ctl/ctl_scsi_all.h>
65229997Sken#include <cam/ctl/ctl_util.h>
66229997Sken#include <cam/ctl/ctl_backend.h>
67229997Sken#include <cam/ctl/ctl_ioctl.h>
68229997Sken
69229997Sken/*
70312841Smav * The default amount of space we allocate for stats storage space.
71312841Smav * We dynamically allocate more if needed.
72229997Sken */
73312841Smav#define	CTL_STAT_NUM_ITEMS	256
74229997Sken
75229997Sken/*
76229997Sken * The default number of LUN selection bits we allocate.  This is large
77229997Sken * because we don't currently increase it if the user specifies a LUN
78229997Sken * number of 1024 or larger.
79229997Sken */
80312841Smav#define	CTL_STAT_BITS		1024L
81229997Sken
82288784Smavstatic const char *ctlstat_opts = "Cc:Ddhjl:n:p:tw:";
83229997Skenstatic const char *ctlstat_usage = "Usage:  ctlstat [-CDdjht] [-l lunnum]"
84229997Sken				   "[-c count] [-n numdevs] [-w wait]\n";
85229997Sken
86229997Skenstruct ctl_cpu_stats {
87229997Sken	uint64_t user;
88229997Sken	uint64_t nice;
89229997Sken	uint64_t system;
90229997Sken	uint64_t intr;
91229997Sken	uint64_t idle;
92229997Sken};
93229997Sken
94229997Skentypedef enum {
95229997Sken	CTLSTAT_MODE_STANDARD,
96229997Sken	CTLSTAT_MODE_DUMP,
97229997Sken	CTLSTAT_MODE_JSON,
98229997Sken} ctlstat_mode_types;
99229997Sken
100229997Sken#define	CTLSTAT_FLAG_CPU		(1 << 0)
101229997Sken#define	CTLSTAT_FLAG_HEADER		(1 << 1)
102229997Sken#define	CTLSTAT_FLAG_FIRST_RUN		(1 << 2)
103229997Sken#define	CTLSTAT_FLAG_TOTALS		(1 << 3)
104229997Sken#define	CTLSTAT_FLAG_DMA_TIME		(1 << 4)
105312841Smav#define	CTLSTAT_FLAG_TIME_VALID		(1 << 5)
106312841Smav#define	CTLSTAT_FLAG_MASK		(1 << 6)
107312841Smav#define	CTLSTAT_FLAG_LUNS		(1 << 7)
108312841Smav#define	CTLSTAT_FLAG_PORTS		(1 << 8)
109229997Sken#define	F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU)
110229997Sken#define	F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER)
111229997Sken#define	F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN)
112229997Sken#define	F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS)
113229997Sken#define	F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME)
114312841Smav#define	F_TIMEVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_TIME_VALID)
115312841Smav#define	F_MASK(ctx) ((ctx)->flags & CTLSTAT_FLAG_MASK)
116312841Smav#define	F_LUNS(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUNS)
117312841Smav#define	F_PORTS(ctx) ((ctx)->flags & CTLSTAT_FLAG_PORTS)
118229997Sken
119229997Skenstruct ctlstat_context {
120229997Sken	ctlstat_mode_types mode;
121229997Sken	int flags;
122312841Smav	struct ctl_io_stats *cur_stats, *prev_stats;
123312841Smav	struct ctl_io_stats cur_total_stats[3], prev_total_stats[3];
124229997Sken	struct timespec cur_time, prev_time;
125229997Sken	struct ctl_cpu_stats cur_cpu, prev_cpu;
126229997Sken	uint64_t cur_total_jiffies, prev_total_jiffies;
127229997Sken	uint64_t cur_idle, prev_idle;
128312841Smav	bitstr_t bit_decl(item_mask, CTL_STAT_BITS);
129312841Smav	int cur_items, prev_items;
130312841Smav	int cur_alloc, prev_alloc;
131229997Sken	int numdevs;
132229997Sken	int header_interval;
133229997Sken};
134229997Sken
135229997Sken#ifndef min
136229997Sken#define	min(x,y)	(((x) < (y)) ? (x) : (y))
137229997Sken#endif
138229997Sken
139229997Skenstatic void usage(int error);
140312841Smavstatic int getstats(int fd, int *alloc_items, int *num_items,
141312841Smav    struct ctl_io_stats **xstats, struct timespec *cur_time, int *time_valid);
142229997Skenstatic int getcpu(struct ctl_cpu_stats *cpu_stats);
143312841Smavstatic void compute_stats(struct ctl_io_stats *cur_stats,
144312841Smav			  struct ctl_io_stats *prev_stats,
145229997Sken			  long double etime, long double *mbsec,
146229997Sken			  long double *kb_per_transfer,
147229997Sken			  long double *transfers_per_second,
148229997Sken			  long double *ms_per_transfer,
149229997Sken			  long double *ms_per_dma,
150229997Sken			  long double *dmas_per_second);
151229997Sken
152229997Skenstatic void
153229997Skenusage(int error)
154229997Sken{
155230034Sken	fputs(ctlstat_usage, error ? stderr : stdout);
156229997Sken}
157229997Sken
158229997Skenstatic int
159312841Smavgetstats(int fd, int *alloc_items, int *num_items, struct ctl_io_stats **stats,
160229997Sken	 struct timespec *cur_time, int *flags)
161229997Sken{
162312841Smav	struct ctl_get_io_stats get_stats;
163312841Smav	int more_space_count = 0;
164229997Sken
165312841Smav	if (*alloc_items == 0)
166312841Smav		*alloc_items = CTL_STAT_NUM_ITEMS;
167229997Skenretry:
168312841Smav	if (*stats == NULL)
169312841Smav		*stats = malloc(sizeof(**stats) * *alloc_items);
170229997Sken
171312841Smav	memset(&get_stats, 0, sizeof(get_stats));
172312841Smav	get_stats.alloc_len = *alloc_items * sizeof(**stats);
173312841Smav	memset(*stats, 0, get_stats.alloc_len);
174312841Smav	get_stats.stats = *stats;
175229997Sken
176312841Smav	if (ioctl(fd, (*flags & CTLSTAT_FLAG_PORTS) ? CTL_GET_PORT_STATS :
177312841Smav	    CTL_GET_LUN_STATS, &get_stats) == -1)
178312841Smav		err(1, "CTL_GET_*_STATS ioctl returned error");
179229997Sken
180312841Smav	switch (get_stats.status) {
181229997Sken	case CTL_SS_OK:
182229997Sken		break;
183229997Sken	case CTL_SS_ERROR:
184312841Smav		err(1, "CTL_GET_*_STATS ioctl returned CTL_SS_ERROR");
185229997Sken		break;
186229997Sken	case CTL_SS_NEED_MORE_SPACE:
187312841Smav		if (more_space_count >= 2)
188312841Smav			errx(1, "CTL_GET_*_STATS returned NEED_MORE_SPACE again");
189312841Smav		*alloc_items = get_stats.num_items * 5 / 4;
190312841Smav		free(*stats);
191312841Smav		*stats = NULL;
192229997Sken		more_space_count++;
193229997Sken		goto retry;
194229997Sken		break; /* NOTREACHED */
195229997Sken	default:
196312841Smav		errx(1, "CTL_GET_*_STATS ioctl returned unknown status %d",
197312841Smav		     get_stats.status);
198229997Sken		break;
199229997Sken	}
200229997Sken
201312841Smav	*num_items = get_stats.fill_len / sizeof(**stats);
202312841Smav	cur_time->tv_sec = get_stats.timestamp.tv_sec;
203312841Smav	cur_time->tv_nsec = get_stats.timestamp.tv_nsec;
204312841Smav	if (get_stats.flags & CTL_STATS_FLAG_TIME_VALID)
205312841Smav		*flags |= CTLSTAT_FLAG_TIME_VALID;
206229997Sken	else
207312841Smav		*flags &= ~CTLSTAT_FLAG_TIME_VALID;
208229997Sken
209229997Sken	return (0);
210229997Sken}
211229997Sken
212229997Skenstatic int
213229997Skengetcpu(struct ctl_cpu_stats *cpu_stats)
214229997Sken{
215229997Sken	long cp_time[CPUSTATES];
216229997Sken	size_t cplen;
217229997Sken
218229997Sken	cplen = sizeof(cp_time);
219229997Sken
220229997Sken	if (sysctlbyname("kern.cp_time", &cp_time, &cplen, NULL, 0) == -1) {
221229997Sken		warn("sysctlbyname(kern.cp_time...) failed");
222229997Sken		return (1);
223229997Sken	}
224229997Sken
225229997Sken	cpu_stats->user = cp_time[CP_USER];
226229997Sken	cpu_stats->nice = cp_time[CP_NICE];
227229997Sken	cpu_stats->system = cp_time[CP_SYS];
228229997Sken	cpu_stats->intr = cp_time[CP_INTR];
229229997Sken	cpu_stats->idle = cp_time[CP_IDLE];
230229997Sken
231229997Sken	return (0);
232229997Sken}
233229997Sken
234229997Skenstatic void
235312841Smavcompute_stats(struct ctl_io_stats *cur_stats,
236312841Smav	      struct ctl_io_stats *prev_stats, long double etime,
237229997Sken	      long double *mbsec, long double *kb_per_transfer,
238229997Sken	      long double *transfers_per_second, long double *ms_per_transfer,
239229997Sken	      long double *ms_per_dma, long double *dmas_per_second)
240229997Sken{
241229997Sken	uint64_t total_bytes = 0, total_operations = 0, total_dmas = 0;
242229997Sken	struct bintime total_time_bt, total_dma_bt;
243229997Sken	struct timespec total_time_ts, total_dma_ts;
244229997Sken	int i;
245229997Sken
246229997Sken	bzero(&total_time_bt, sizeof(total_time_bt));
247229997Sken	bzero(&total_dma_bt, sizeof(total_dma_bt));
248229997Sken	bzero(&total_time_ts, sizeof(total_time_ts));
249229997Sken	bzero(&total_dma_ts, sizeof(total_dma_ts));
250312841Smav	for (i = 0; i < CTL_STATS_NUM_TYPES; i++) {
251312841Smav		total_bytes += cur_stats->bytes[i];
252312841Smav		total_operations += cur_stats->operations[i];
253312841Smav		total_dmas += cur_stats->dmas[i];
254312841Smav		bintime_add(&total_time_bt, &cur_stats->time[i]);
255312841Smav		bintime_add(&total_dma_bt, &cur_stats->dma_time[i]);
256312841Smav		if (prev_stats != NULL) {
257312841Smav			total_bytes -= prev_stats->bytes[i];
258312841Smav			total_operations -= prev_stats->operations[i];
259312841Smav			total_dmas -= prev_stats->dmas[i];
260312841Smav			bintime_sub(&total_time_bt, &prev_stats->time[i]);
261312841Smav			bintime_sub(&total_dma_bt, &prev_stats->dma_time[i]);
262229997Sken		}
263229997Sken	}
264229997Sken
265229997Sken	*mbsec = total_bytes;
266229997Sken	*mbsec /= 1024 * 1024;
267229997Sken	if (etime > 0.0)
268229997Sken		*mbsec /= etime;
269229997Sken	else
270229997Sken		*mbsec = 0;
271229997Sken	*kb_per_transfer = total_bytes;
272229997Sken	*kb_per_transfer /= 1024;
273229997Sken	if (total_operations > 0)
274229997Sken		*kb_per_transfer /= total_operations;
275229997Sken	else
276229997Sken		*kb_per_transfer = 0;
277229997Sken	*transfers_per_second = total_operations;
278229997Sken	*dmas_per_second = total_dmas;
279229997Sken	if (etime > 0.0) {
280229997Sken		*transfers_per_second /= etime;
281229997Sken		*dmas_per_second /= etime;
282229997Sken	} else {
283229997Sken		*transfers_per_second = 0;
284229997Sken		*dmas_per_second = 0;
285229997Sken	}
286229997Sken
287229997Sken	bintime2timespec(&total_time_bt, &total_time_ts);
288229997Sken	bintime2timespec(&total_dma_bt, &total_dma_ts);
289229997Sken	if (total_operations > 0) {
290229997Sken		/*
291229997Sken		 * Convert the timespec to milliseconds.
292229997Sken		 */
293229997Sken		*ms_per_transfer = total_time_ts.tv_sec * 1000;
294229997Sken		*ms_per_transfer += total_time_ts.tv_nsec / 1000000;
295229997Sken		*ms_per_transfer /= total_operations;
296229997Sken	} else
297229997Sken		*ms_per_transfer = 0;
298229997Sken
299229997Sken	if (total_dmas > 0) {
300229997Sken		/*
301229997Sken		 * Convert the timespec to milliseconds.
302229997Sken		 */
303229997Sken		*ms_per_dma = total_dma_ts.tv_sec * 1000;
304229997Sken		*ms_per_dma += total_dma_ts.tv_nsec / 1000000;
305229997Sken		*ms_per_dma /= total_dmas;
306229997Sken	} else
307229997Sken		*ms_per_dma = 0;
308229997Sken}
309229997Sken
310229997Sken/* The dump_stats() and json_stats() functions perform essentially the same
311229997Sken * purpose, but dump the statistics in different formats.  JSON is more
312229997Sken * conducive to programming, however.
313229997Sken */
314229997Sken
315314992Smav#define	PRINT_BINTIME(bt) \
316314992Smav	printf("%jd.%06ju", (intmax_t)(bt).sec, \
317288783Smav	       (uintmax_t)(((bt).frac >> 32) * 1000000 >> 32))
318241737Sedstatic const char *iotypes[] = {"NO IO", "READ", "WRITE"};
319229997Sken
320229997Skenstatic void
321312841Smavctlstat_dump(struct ctlstat_context *ctx)
322312841Smav{
323312841Smav	int iotype, i;
324312841Smav	struct ctl_io_stats *stats = ctx->cur_stats;
325229997Sken
326312841Smav	for (i = 0; i < ctx->cur_items;i++) {
327312841Smav		if (F_MASK(ctx) && bit_test(ctx->item_mask, i) == 0)
328288784Smav			continue;
329312841Smav		printf("%s %d\n", F_PORTS(ctx) ? "port" : "lun", stats[i].item);
330312841Smav		for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) {
331312841Smav			printf("  io type %d (%s)\n", iotype, iotypes[iotype]);
332312841Smav			printf("   bytes %ju\n", (uintmax_t)
333312841Smav			    stats[i].bytes[iotype]);
334312841Smav			printf("   operations %ju\n", (uintmax_t)
335312841Smav			    stats[i].operations[iotype]);
336312841Smav			printf("   dmas %ju\n", (uintmax_t)
337312841Smav			    stats[i].dmas[iotype]);
338314992Smav			printf("   io time ");
339314992Smav			PRINT_BINTIME(stats[i].time[iotype]);
340314992Smav			printf("\n   dma time ");
341314992Smav			PRINT_BINTIME(stats[i].dma_time[iotype]);
342314992Smav			printf("\n");
343229997Sken		}
344229997Sken	}
345229997Sken}
346229997Sken
347229997Skenstatic void
348229997Skenctlstat_json(struct ctlstat_context *ctx) {
349312841Smav	int iotype, i;
350312841Smav	struct ctl_io_stats *stats = ctx->cur_stats;
351229997Sken
352312841Smav	printf("{\"%s\":[", F_PORTS(ctx) ? "ports" : "luns");
353312841Smav	for (i = 0; i < ctx->cur_items; i++) {
354312841Smav		if (F_MASK(ctx) && bit_test(ctx->item_mask, i) == 0)
355288784Smav			continue;
356312841Smav		printf("{\"num\":%d,\"io\":[",
357312841Smav		    stats[i].item);
358312841Smav		for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) {
359312841Smav			printf("{\"type\":\"%s\",", iotypes[iotype]);
360314992Smav			printf("\"bytes\":%ju,", (uintmax_t)
361314992Smav			    stats[i].bytes[iotype]);
362314992Smav			printf("\"operations\":%ju,", (uintmax_t)
363314992Smav			    stats[i].operations[iotype]);
364314992Smav			printf("\"dmas\":%ju,", (uintmax_t)
365312841Smav			    stats[i].dmas[iotype]);
366314992Smav			printf("\"io time\":");
367314992Smav			PRINT_BINTIME(stats[i].time[iotype]);
368314992Smav			printf(",\"dma time\":");
369314992Smav			PRINT_BINTIME(stats[i].dma_time[iotype]);
370314992Smav			printf("}");
371312841Smav			if (iotype < (CTL_STATS_NUM_TYPES - 1))
372312841Smav				printf(","); /* continue io array */
373229997Sken		}
374312841Smav		printf("]}");
375312841Smav		if (i < (ctx->cur_items - 1))
376229997Sken			printf(","); /* continue lun array */
377229997Sken	}
378312841Smav	printf("]}");
379229997Sken}
380229997Sken
381229997Skenstatic void
382229997Skenctlstat_standard(struct ctlstat_context *ctx) {
383249384Sken	long double etime;
384229997Sken	uint64_t delta_jiffies, delta_idle;
385229997Sken	long double cpu_percentage;
386312841Smav	int i, j;
387229997Sken
388229997Sken	cpu_percentage = 0;
389229997Sken
390229997Sken	if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0))
391229997Sken		errx(1, "error returned from getcpu()");
392229997Sken
393312841Smav	etime = ctx->cur_time.tv_sec - ctx->prev_time.tv_sec +
394312841Smav	    (ctx->prev_time.tv_nsec - ctx->cur_time.tv_nsec) * 1e-9;
395229997Sken
396229997Sken	if (F_CPU(ctx)) {
397229997Sken		ctx->prev_total_jiffies = ctx->cur_total_jiffies;
398229997Sken		ctx->cur_total_jiffies = ctx->cur_cpu.user +
399229997Sken		    ctx->cur_cpu.nice + ctx->cur_cpu.system +
400229997Sken		    ctx->cur_cpu.intr + ctx->cur_cpu.idle;
401229997Sken		delta_jiffies = ctx->cur_total_jiffies;
402229997Sken		if (F_FIRST(ctx) == 0)
403229997Sken			delta_jiffies -= ctx->prev_total_jiffies;
404229997Sken		ctx->prev_idle = ctx->cur_idle;
405229997Sken		ctx->cur_idle = ctx->cur_cpu.idle;
406229997Sken		delta_idle = ctx->cur_idle - ctx->prev_idle;
407229997Sken
408229997Sken		cpu_percentage = delta_jiffies - delta_idle;
409229997Sken		cpu_percentage /= delta_jiffies;
410229997Sken		cpu_percentage *= 100;
411229997Sken	}
412229997Sken
413229997Sken	if (F_HDR(ctx)) {
414229997Sken		ctx->header_interval--;
415229997Sken		if (ctx->header_interval <= 0) {
416229997Sken			int hdr_devs;
417229997Sken
418229997Sken			hdr_devs = 0;
419229997Sken
420288784Smav			if (F_CPU(ctx))
421288784Smav				fprintf(stdout, " CPU");
422229997Sken			if (F_TOTALS(ctx)) {
423288784Smav				fprintf(stdout, "%s     Read       %s"
424288784Smav					"    Write       %s    Total\n",
425312841Smav					(F_TIMEVAL(ctx) != 0) ? "      " : "",
426312841Smav					(F_TIMEVAL(ctx) != 0) ? "      " : "",
427312841Smav					(F_TIMEVAL(ctx) != 0) ? "      " : "");
428229997Sken				hdr_devs = 3;
429229997Sken			} else {
430312841Smav				for (i = 0; i < min(CTL_STAT_BITS,
431312841Smav				     ctx->cur_items); i++) {
432312841Smav					int item;
433229997Sken
434229997Sken					/*
435229997Sken					 * Obviously this won't work with
436229997Sken					 * LUN numbers greater than a signed
437229997Sken					 * integer.
438229997Sken					 */
439312841Smav					item = (int)ctx->cur_stats[i].item;
440229997Sken
441312841Smav					if (F_MASK(ctx) &&
442312841Smav					    bit_test(ctx->item_mask, item) == 0)
443229997Sken						continue;
444278747Smav					fprintf(stdout, "%15.6s%d %s",
445312841Smav					    F_PORTS(ctx) ? "port" : "lun", item,
446312841Smav					    (F_TIMEVAL(ctx) != 0) ? "     " : "");
447229997Sken					hdr_devs++;
448229997Sken				}
449229997Sken				fprintf(stdout, "\n");
450229997Sken			}
451288784Smav			if (F_CPU(ctx))
452288784Smav				fprintf(stdout, "    ");
453229997Sken			for (i = 0; i < hdr_devs; i++)
454288784Smav				fprintf(stdout, "%s KB/t   %s MB/s",
455312841Smav					(F_TIMEVAL(ctx) != 0) ? "    ms" : "",
456229997Sken					(F_DMA(ctx) == 0) ? "tps" : "dps");
457229997Sken			fprintf(stdout, "\n");
458229997Sken			ctx->header_interval = 20;
459229997Sken		}
460229997Sken	}
461229997Sken
462288784Smav	if (F_CPU(ctx))
463288784Smav		fprintf(stdout, "%3.0Lf%%", cpu_percentage);
464229997Sken	if (F_TOTALS(ctx) != 0) {
465229997Sken		long double mbsec[3];
466229997Sken		long double kb_per_transfer[3];
467229997Sken		long double transfers_per_sec[3];
468229997Sken		long double ms_per_transfer[3];
469229997Sken		long double ms_per_dma[3];
470229997Sken		long double dmas_per_sec[3];
471229997Sken
472229997Sken		for (i = 0; i < 3; i++)
473229997Sken			ctx->prev_total_stats[i] = ctx->cur_total_stats[i];
474229997Sken
475229997Sken		memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats));
476229997Sken
477229997Sken		/* Use macros to make the next loop more readable. */
478312841Smav#define	ADD_STATS_BYTES(st, i, j) \
479312841Smav	ctx->cur_total_stats[st].bytes[j] += \
480312841Smav	    ctx->cur_stats[i].bytes[j]
481312841Smav#define	ADD_STATS_OPERATIONS(st, i, j) \
482312841Smav	ctx->cur_total_stats[st].operations[j] += \
483312841Smav	    ctx->cur_stats[i].operations[j]
484312841Smav#define	ADD_STATS_DMAS(st, i, j) \
485312841Smav	ctx->cur_total_stats[st].dmas[j] += \
486312841Smav	    ctx->cur_stats[i].dmas[j]
487312841Smav#define	ADD_STATS_TIME(st, i, j) \
488312841Smav	bintime_add(&ctx->cur_total_stats[st].time[j], \
489312841Smav	    &ctx->cur_stats[i].time[j])
490312841Smav#define	ADD_STATS_DMA_TIME(st, i, j) \
491312841Smav	bintime_add(&ctx->cur_total_stats[st].dma_time[j], \
492312841Smav	    &ctx->cur_stats[i].dma_time[j])
493229997Sken
494312841Smav		for (i = 0; i < ctx->cur_items; i++) {
495312841Smav			if (F_MASK(ctx) && bit_test(ctx->item_mask,
496312841Smav			    (int)ctx->cur_stats[i].item) == 0)
497288784Smav				continue;
498312841Smav			for (j = 0; j < CTL_STATS_NUM_TYPES; j++) {
499312841Smav				ADD_STATS_BYTES(2, i, j);
500312841Smav				ADD_STATS_OPERATIONS(2, i, j);
501312841Smav				ADD_STATS_DMAS(2, i, j);
502312841Smav				ADD_STATS_TIME(2, i, j);
503312841Smav				ADD_STATS_DMA_TIME(2, i, j);
504312841Smav			}
505312841Smav			ADD_STATS_BYTES(0, i, CTL_STATS_READ);
506312841Smav			ADD_STATS_OPERATIONS(0, i, CTL_STATS_READ);
507312841Smav			ADD_STATS_DMAS(0, i, CTL_STATS_READ);
508312841Smav			ADD_STATS_TIME(0, i, CTL_STATS_READ);
509312841Smav			ADD_STATS_DMA_TIME(0, i, CTL_STATS_READ);
510229997Sken
511312841Smav			ADD_STATS_BYTES(1, i, CTL_STATS_WRITE);
512312841Smav			ADD_STATS_OPERATIONS(1, i, CTL_STATS_WRITE);
513312841Smav			ADD_STATS_DMAS(1, i, CTL_STATS_WRITE);
514312841Smav			ADD_STATS_TIME(1, i, CTL_STATS_WRITE);
515312841Smav			ADD_STATS_DMA_TIME(1, i, CTL_STATS_WRITE);
516229997Sken		}
517229997Sken
518229997Sken		for (i = 0; i < 3; i++) {
519312841Smav			compute_stats(&ctx->cur_total_stats[i],
520229997Sken				F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i],
521229997Sken				etime, &mbsec[i], &kb_per_transfer[i],
522229997Sken				&transfers_per_sec[i],
523229997Sken				&ms_per_transfer[i], &ms_per_dma[i],
524229997Sken				&dmas_per_sec[i]);
525229997Sken			if (F_DMA(ctx) != 0)
526288784Smav				fprintf(stdout, " %5.1Lf",
527229997Sken					ms_per_dma[i]);
528312841Smav			else if (F_TIMEVAL(ctx) != 0)
529288784Smav				fprintf(stdout, " %5.1Lf",
530229997Sken					ms_per_transfer[i]);
531288784Smav			fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf",
532229997Sken				kb_per_transfer[i],
533229997Sken				(F_DMA(ctx) == 0) ? transfers_per_sec[i] :
534229997Sken				dmas_per_sec[i], mbsec[i]);
535229997Sken		}
536229997Sken	} else {
537312841Smav		for (i = 0; i < min(CTL_STAT_BITS, ctx->cur_items); i++) {
538229997Sken			long double mbsec, kb_per_transfer;
539229997Sken			long double transfers_per_sec;
540229997Sken			long double ms_per_transfer;
541229997Sken			long double ms_per_dma;
542229997Sken			long double dmas_per_sec;
543229997Sken
544312841Smav			if (F_MASK(ctx) && bit_test(ctx->item_mask,
545312841Smav			    (int)ctx->cur_stats[i].item) == 0)
546229997Sken				continue;
547312841Smav			for (j = 0; j < ctx->prev_items; j++) {
548312841Smav				if (ctx->prev_stats[j].item ==
549312841Smav				    ctx->cur_stats[i].item)
550312841Smav					break;
551312841Smav			}
552312841Smav			if (j >= ctx->prev_items)
553312841Smav				j = -1;
554312841Smav			compute_stats(&ctx->cur_stats[i],
555312841Smav			    j >= 0 ? &ctx->prev_stats[j] : NULL,
556288784Smav			    etime, &mbsec, &kb_per_transfer,
557288784Smav			    &transfers_per_sec, &ms_per_transfer,
558288784Smav			    &ms_per_dma, &dmas_per_sec);
559229997Sken			if (F_DMA(ctx))
560288784Smav				fprintf(stdout, " %5.1Lf",
561229997Sken					ms_per_dma);
562312841Smav			else if (F_TIMEVAL(ctx) != 0)
563288784Smav				fprintf(stdout, " %5.1Lf",
564229997Sken					ms_per_transfer);
565288784Smav			fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf",
566229997Sken				kb_per_transfer, (F_DMA(ctx) == 0) ?
567229997Sken				transfers_per_sec : dmas_per_sec, mbsec);
568229997Sken		}
569229997Sken	}
570229997Sken}
571229997Sken
572229997Skenint
573229997Skenmain(int argc, char **argv)
574229997Sken{
575229997Sken	int c;
576229997Sken	int count, waittime;
577229997Sken	int fd, retval;
578229997Sken	struct ctlstat_context ctx;
579312841Smav	struct ctl_io_stats *tmp_stats;
580229997Sken
581229997Sken	/* default values */
582229997Sken	retval = 0;
583229997Sken	waittime = 1;
584229997Sken	count = -1;
585229997Sken	memset(&ctx, 0, sizeof(ctx));
586229997Sken	ctx.numdevs = 3;
587229997Sken	ctx.mode = CTLSTAT_MODE_STANDARD;
588229997Sken	ctx.flags |= CTLSTAT_FLAG_CPU;
589229997Sken	ctx.flags |= CTLSTAT_FLAG_FIRST_RUN;
590229997Sken	ctx.flags |= CTLSTAT_FLAG_HEADER;
591229997Sken
592229997Sken	while ((c = getopt(argc, argv, ctlstat_opts)) != -1) {
593229997Sken		switch (c) {
594229997Sken		case 'C':
595229997Sken			ctx.flags &= ~CTLSTAT_FLAG_CPU;
596229997Sken			break;
597229997Sken		case 'c':
598229997Sken			count = atoi(optarg);
599229997Sken			break;
600229997Sken		case 'd':
601229997Sken			ctx.flags |= CTLSTAT_FLAG_DMA_TIME;
602229997Sken			break;
603229997Sken		case 'D':
604229997Sken			ctx.mode = CTLSTAT_MODE_DUMP;
605229997Sken			waittime = 30;
606229997Sken			break;
607229997Sken		case 'h':
608229997Sken			ctx.flags &= ~CTLSTAT_FLAG_HEADER;
609229997Sken			break;
610229997Sken		case 'j':
611229997Sken			ctx.mode = CTLSTAT_MODE_JSON;
612229997Sken			waittime = 30;
613229997Sken			break;
614229997Sken		case 'l': {
615229997Sken			int cur_lun;
616229997Sken
617229997Sken			cur_lun = atoi(optarg);
618312841Smav			if (cur_lun > CTL_STAT_BITS)
619229997Sken				errx(1, "Invalid LUN number %d", cur_lun);
620229997Sken
621312841Smav			if (!F_MASK(&ctx))
622229997Sken				ctx.numdevs = 1;
623229997Sken			else
624229997Sken				ctx.numdevs++;
625312841Smav			bit_set(ctx.item_mask, cur_lun);
626312841Smav			ctx.flags |= CTLSTAT_FLAG_MASK;
627312841Smav			ctx.flags |= CTLSTAT_FLAG_LUNS;
628229997Sken			break;
629229997Sken		}
630229997Sken		case 'n':
631229997Sken			ctx.numdevs = atoi(optarg);
632229997Sken			break;
633288784Smav		case 'p': {
634288784Smav			int cur_port;
635288784Smav
636288784Smav			cur_port = atoi(optarg);
637312841Smav			if (cur_port > CTL_STAT_BITS)
638312841Smav				errx(1, "Invalid port number %d", cur_port);
639288784Smav
640312841Smav			if (!F_MASK(&ctx))
641312841Smav				ctx.numdevs = 1;
642312841Smav			else
643312841Smav				ctx.numdevs++;
644312841Smav			bit_set(ctx.item_mask, cur_port);
645312841Smav			ctx.flags |= CTLSTAT_FLAG_MASK;
646312841Smav			ctx.flags |= CTLSTAT_FLAG_PORTS;
647288784Smav			break;
648288784Smav		}
649229997Sken		case 't':
650229997Sken			ctx.flags |= CTLSTAT_FLAG_TOTALS;
651229997Sken			break;
652229997Sken		case 'w':
653229997Sken			waittime = atoi(optarg);
654229997Sken			break;
655229997Sken		default:
656229997Sken			retval = 1;
657229997Sken			usage(retval);
658229997Sken			exit(retval);
659229997Sken			break;
660229997Sken		}
661229997Sken	}
662229997Sken
663312841Smav	if (F_LUNS(&ctx) && F_PORTS(&ctx))
664312841Smav		errx(1, "Options -p and -l are exclusive.");
665312841Smav
666312841Smav	if (!F_LUNS(&ctx) && !F_PORTS(&ctx)) {
667312841Smav		if (F_TOTALS(&ctx))
668312841Smav			ctx.flags |= CTLSTAT_FLAG_PORTS;
669312841Smav		else
670312841Smav			ctx.flags |= CTLSTAT_FLAG_LUNS;
671312841Smav	}
672312841Smav
673312841Smav	if (!F_TOTALS(&ctx) && !F_MASK(&ctx)) {
674229997Sken		/*
675229997Sken		 * Note that this just selects the first N LUNs to display,
676229997Sken		 * but at this point we have no knoweledge of which LUN
677229997Sken		 * numbers actually exist.  So we may select LUNs that
678229997Sken		 * aren't there.
679229997Sken		 */
680312841Smav		bit_nset(ctx.item_mask, 0, min(ctx.numdevs - 1,
681312841Smav			 CTL_STAT_BITS - 1));
682312841Smav		ctx.flags |= CTLSTAT_FLAG_MASK;
683229997Sken	}
684229997Sken
685229997Sken	if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1)
686229997Sken		err(1, "cannot open %s", CTL_DEFAULT_DEV);
687229997Sken
688229997Sken	for (;count != 0;) {
689312841Smav		tmp_stats = ctx.prev_stats;
690312841Smav		ctx.prev_stats = ctx.cur_stats;
691312841Smav		ctx.cur_stats = tmp_stats;
692312841Smav		c = ctx.prev_alloc;
693312841Smav		ctx.prev_alloc = ctx.cur_alloc;
694312841Smav		ctx.cur_alloc = c;
695312841Smav		c = ctx.prev_items;
696312841Smav		ctx.prev_items = ctx.cur_items;
697312841Smav		ctx.cur_items = c;
698229997Sken		ctx.prev_time = ctx.cur_time;
699229997Sken		ctx.prev_cpu = ctx.cur_cpu;
700312841Smav		if (getstats(fd, &ctx.cur_alloc, &ctx.cur_items,
701312841Smav		    &ctx.cur_stats, &ctx.cur_time, &ctx.flags) != 0)
702229997Sken			errx(1, "error returned from getstats()");
703229997Sken
704229997Sken		switch(ctx.mode) {
705229997Sken		case CTLSTAT_MODE_STANDARD:
706229997Sken			ctlstat_standard(&ctx);
707229997Sken			break;
708229997Sken		case CTLSTAT_MODE_DUMP:
709229997Sken			ctlstat_dump(&ctx);
710229997Sken			break;
711229997Sken		case CTLSTAT_MODE_JSON:
712229997Sken			ctlstat_json(&ctx);
713229997Sken			break;
714229997Sken		default:
715229997Sken			break;
716229997Sken		}
717229997Sken
718229997Sken		fprintf(stdout, "\n");
719229997Sken		ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN;
720229997Sken		if (count != 1)
721229997Sken			sleep(waittime);
722229997Sken		if (count > 0)
723229997Sken			count--;
724229997Sken	}
725229997Sken
726229997Sken	exit (retval);
727229997Sken}
728229997Sken
729229997Sken/*
730229997Sken * vim: ts=8
731229997Sken */
732