xref: /freebsd/usr.bin/ctlstat/ctlstat.c (revision 5e3934b1)
1130f4520SKenneth D. Merry /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4130f4520SKenneth D. Merry  * Copyright (c) 2004, 2008, 2009 Silicon Graphics International Corp.
5bb8f9017SAlexander Motin  * Copyright (c) 2017 Alexander Motin <mav@FreeBSD.org>
6130f4520SKenneth D. Merry  * All rights reserved.
7130f4520SKenneth D. Merry  *
8130f4520SKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
9130f4520SKenneth D. Merry  * modification, are permitted provided that the following conditions
10130f4520SKenneth D. Merry  * are met:
11130f4520SKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright
12130f4520SKenneth D. Merry  *    notice, this list of conditions, and the following disclaimer,
13130f4520SKenneth D. Merry  *    without modification.
14130f4520SKenneth D. Merry  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
15130f4520SKenneth D. Merry  *    substantially similar to the "NO WARRANTY" disclaimer below
16130f4520SKenneth D. Merry  *    ("Disclaimer") and any redistribution must be conditioned upon
17130f4520SKenneth D. Merry  *    including a substantially similar Disclaimer requirement for further
18130f4520SKenneth D. Merry  *    binary redistribution.
19130f4520SKenneth D. Merry  *
20130f4520SKenneth D. Merry  * NO WARRANTY
21130f4520SKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22130f4520SKenneth D. Merry  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23130f4520SKenneth D. Merry  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
24130f4520SKenneth D. Merry  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25130f4520SKenneth D. Merry  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26130f4520SKenneth D. Merry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27130f4520SKenneth D. Merry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28130f4520SKenneth D. Merry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29130f4520SKenneth D. Merry  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30130f4520SKenneth D. Merry  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31130f4520SKenneth D. Merry  * POSSIBILITY OF SUCH DAMAGES.
32130f4520SKenneth D. Merry  *
33130f4520SKenneth D. Merry  * $Id: //depot/users/kenm/FreeBSD-test2/usr.bin/ctlstat/ctlstat.c#4 $
34130f4520SKenneth D. Merry  */
35130f4520SKenneth D. Merry /*
36130f4520SKenneth D. Merry  * CAM Target Layer statistics program
37130f4520SKenneth D. Merry  *
38130f4520SKenneth D. Merry  * Authors: Ken Merry <ken@FreeBSD.org>, Will Andrews <will@FreeBSD.org>
39130f4520SKenneth D. Merry  */
40130f4520SKenneth D. Merry 
41130f4520SKenneth D. Merry #include <sys/param.h>
42130f4520SKenneth D. Merry #include <sys/callout.h>
431a7f22d9SAlan Somers #include <sys/ioctl.h>
441a7f22d9SAlan Somers #include <sys/queue.h>
451a7f22d9SAlan Somers #include <sys/resource.h>
461a7f22d9SAlan Somers #include <sys/sbuf.h>
471a7f22d9SAlan Somers #include <sys/socket.h>
481a7f22d9SAlan Somers #include <sys/sysctl.h>
491a7f22d9SAlan Somers #include <sys/time.h>
504c163a54SAlan Somers #include <assert.h>
511a7f22d9SAlan Somers #include <bsdxml.h>
521a7f22d9SAlan Somers #include <malloc_np.h>
53130f4520SKenneth D. Merry #include <stdint.h>
54130f4520SKenneth D. Merry #include <stdio.h>
55130f4520SKenneth D. Merry #include <stdlib.h>
56130f4520SKenneth D. Merry #include <unistd.h>
57130f4520SKenneth D. Merry #include <fcntl.h>
581a7f22d9SAlan Somers #include <inttypes.h>
59130f4520SKenneth D. Merry #include <getopt.h>
60130f4520SKenneth D. Merry #include <string.h>
61130f4520SKenneth D. Merry #include <errno.h>
62130f4520SKenneth D. Merry #include <err.h>
63130f4520SKenneth D. Merry #include <ctype.h>
64130f4520SKenneth D. Merry #include <bitstring.h>
65130f4520SKenneth D. Merry #include <cam/scsi/scsi_all.h>
66130f4520SKenneth D. Merry #include <cam/ctl/ctl.h>
67130f4520SKenneth D. Merry #include <cam/ctl/ctl_io.h>
68130f4520SKenneth D. Merry #include <cam/ctl/ctl_scsi_all.h>
69130f4520SKenneth D. Merry #include <cam/ctl/ctl_util.h>
70130f4520SKenneth D. Merry #include <cam/ctl/ctl_backend.h>
71130f4520SKenneth D. Merry #include <cam/ctl/ctl_ioctl.h>
72130f4520SKenneth D. Merry 
73130f4520SKenneth D. Merry /*
74bb8f9017SAlexander Motin  * The default amount of space we allocate for stats storage space.
75bb8f9017SAlexander Motin  * We dynamically allocate more if needed.
76130f4520SKenneth D. Merry  */
77bb8f9017SAlexander Motin #define	CTL_STAT_NUM_ITEMS	256
78130f4520SKenneth D. Merry 
795e4b2529SBaptiste Daroussin static int ctl_stat_bits;
80130f4520SKenneth D. Merry 
811a7f22d9SAlan Somers static const char *ctlstat_opts = "Cc:DPdhjl:n:p:tw:";
821a7f22d9SAlan Somers static const char *ctlstat_usage = "Usage:  ctlstat [-CDPdjht] [-l lunnum]"
83130f4520SKenneth D. Merry 				   "[-c count] [-n numdevs] [-w wait]\n";
84130f4520SKenneth D. Merry 
85130f4520SKenneth D. Merry struct ctl_cpu_stats {
86130f4520SKenneth D. Merry 	uint64_t user;
87130f4520SKenneth D. Merry 	uint64_t nice;
88130f4520SKenneth D. Merry 	uint64_t system;
89130f4520SKenneth D. Merry 	uint64_t intr;
90130f4520SKenneth D. Merry 	uint64_t idle;
91130f4520SKenneth D. Merry };
92130f4520SKenneth D. Merry 
93130f4520SKenneth D. Merry typedef enum {
94130f4520SKenneth D. Merry 	CTLSTAT_MODE_STANDARD,
95130f4520SKenneth D. Merry 	CTLSTAT_MODE_DUMP,
96130f4520SKenneth D. Merry 	CTLSTAT_MODE_JSON,
971a7f22d9SAlan Somers 	CTLSTAT_MODE_PROMETHEUS,
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)
105bb8f9017SAlexander Motin #define	CTLSTAT_FLAG_TIME_VALID		(1 << 5)
106bb8f9017SAlexander Motin #define	CTLSTAT_FLAG_MASK		(1 << 6)
107bb8f9017SAlexander Motin #define	CTLSTAT_FLAG_LUNS		(1 << 7)
108bb8f9017SAlexander Motin #define	CTLSTAT_FLAG_PORTS		(1 << 8)
109130f4520SKenneth D. Merry #define	F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU)
110130f4520SKenneth D. Merry #define	F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER)
111130f4520SKenneth D. Merry #define	F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN)
112130f4520SKenneth D. Merry #define	F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS)
113130f4520SKenneth D. Merry #define	F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME)
114bb8f9017SAlexander Motin #define	F_TIMEVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_TIME_VALID)
115bb8f9017SAlexander Motin #define	F_MASK(ctx) ((ctx)->flags & CTLSTAT_FLAG_MASK)
116bb8f9017SAlexander Motin #define	F_LUNS(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUNS)
117bb8f9017SAlexander Motin #define	F_PORTS(ctx) ((ctx)->flags & CTLSTAT_FLAG_PORTS)
118130f4520SKenneth D. Merry 
119130f4520SKenneth D. Merry struct ctlstat_context {
120130f4520SKenneth D. Merry 	ctlstat_mode_types mode;
121130f4520SKenneth D. Merry 	int flags;
122bb8f9017SAlexander Motin 	struct ctl_io_stats *cur_stats, *prev_stats;
123bb8f9017SAlexander Motin 	struct ctl_io_stats cur_total_stats[3], prev_total_stats[3];
124130f4520SKenneth D. Merry 	struct timespec cur_time, prev_time;
125130f4520SKenneth D. Merry 	struct ctl_cpu_stats cur_cpu, prev_cpu;
126130f4520SKenneth D. Merry 	uint64_t cur_total_jiffies, prev_total_jiffies;
127130f4520SKenneth D. Merry 	uint64_t cur_idle, prev_idle;
1285e4b2529SBaptiste Daroussin 	bitstr_t *item_mask;
129bb8f9017SAlexander Motin 	int cur_items, prev_items;
130bb8f9017SAlexander Motin 	int cur_alloc, prev_alloc;
131130f4520SKenneth D. Merry 	int numdevs;
132130f4520SKenneth D. Merry 	int header_interval;
133130f4520SKenneth D. Merry };
134130f4520SKenneth D. Merry 
1351a7f22d9SAlan Somers struct cctl_portlist_data {
1361a7f22d9SAlan Somers 	int level;
1371a7f22d9SAlan Somers 	struct sbuf *cur_sb[32];
1384c163a54SAlan Somers 	int id;
1391a7f22d9SAlan Somers 	int lun;
1401a7f22d9SAlan Somers 	int ntargets;
1411a7f22d9SAlan Somers 	char *target;
1421a7f22d9SAlan Somers 	char **targets;
1431a7f22d9SAlan Somers };
1441a7f22d9SAlan Somers 
145130f4520SKenneth D. Merry #ifndef min
146130f4520SKenneth D. Merry #define	min(x,y)	(((x) < (y)) ? (x) : (y))
147130f4520SKenneth D. Merry #endif
148130f4520SKenneth D. Merry 
149130f4520SKenneth D. Merry static void usage(int error);
150bb8f9017SAlexander Motin static int getstats(int fd, int *alloc_items, int *num_items,
1514c163a54SAlan Somers     struct ctl_io_stats **xstats, struct timespec *cur_time, int *time_valid,
1524c163a54SAlan Somers     bool ports);
153130f4520SKenneth D. Merry static int getcpu(struct ctl_cpu_stats *cpu_stats);
154bb8f9017SAlexander Motin static void compute_stats(struct ctl_io_stats *cur_stats,
155bb8f9017SAlexander Motin 			  struct ctl_io_stats *prev_stats,
156130f4520SKenneth D. Merry 			  long double etime, long double *mbsec,
157130f4520SKenneth D. Merry 			  long double *kb_per_transfer,
158130f4520SKenneth D. Merry 			  long double *transfers_per_second,
159130f4520SKenneth D. Merry 			  long double *ms_per_transfer,
160130f4520SKenneth D. Merry 			  long double *ms_per_dma,
161130f4520SKenneth D. Merry 			  long double *dmas_per_second);
162130f4520SKenneth D. Merry 
163130f4520SKenneth D. Merry static void
usage(int error)164130f4520SKenneth D. Merry usage(int error)
165130f4520SKenneth D. Merry {
16633d35bebSKenneth D. Merry 	fputs(ctlstat_usage, error ? stderr : stdout);
167130f4520SKenneth D. Merry }
168130f4520SKenneth D. Merry 
169130f4520SKenneth D. Merry static int
getstats(int fd,int * alloc_items,int * num_items,struct ctl_io_stats ** stats,struct timespec * cur_time,int * flags,bool ports)170bb8f9017SAlexander Motin getstats(int fd, int *alloc_items, int *num_items, struct ctl_io_stats **stats,
1714c163a54SAlan Somers 	 struct timespec *cur_time, int *flags, bool ports)
172130f4520SKenneth D. Merry {
173bb8f9017SAlexander Motin 	struct ctl_get_io_stats get_stats;
174bb8f9017SAlexander Motin 	int more_space_count = 0;
175130f4520SKenneth D. Merry 
176bb8f9017SAlexander Motin 	if (*alloc_items == 0)
177bb8f9017SAlexander Motin 		*alloc_items = CTL_STAT_NUM_ITEMS;
178130f4520SKenneth D. Merry retry:
179bb8f9017SAlexander Motin 	if (*stats == NULL)
180bb8f9017SAlexander Motin 		*stats = malloc(sizeof(**stats) * *alloc_items);
181130f4520SKenneth D. Merry 
182bb8f9017SAlexander Motin 	memset(&get_stats, 0, sizeof(get_stats));
183bb8f9017SAlexander Motin 	get_stats.alloc_len = *alloc_items * sizeof(**stats);
184bb8f9017SAlexander Motin 	memset(*stats, 0, get_stats.alloc_len);
185bb8f9017SAlexander Motin 	get_stats.stats = *stats;
186130f4520SKenneth D. Merry 
1874c163a54SAlan Somers 	if (ioctl(fd, ports ? CTL_GET_PORT_STATS : CTL_GET_LUN_STATS,
1884c163a54SAlan Somers 	    &get_stats) == -1)
189bb8f9017SAlexander Motin 		err(1, "CTL_GET_*_STATS ioctl returned error");
190130f4520SKenneth D. Merry 
191bb8f9017SAlexander Motin 	switch (get_stats.status) {
192130f4520SKenneth D. Merry 	case CTL_SS_OK:
193130f4520SKenneth D. Merry 		break;
194130f4520SKenneth D. Merry 	case CTL_SS_ERROR:
195bb8f9017SAlexander Motin 		err(1, "CTL_GET_*_STATS ioctl returned CTL_SS_ERROR");
196130f4520SKenneth D. Merry 		break;
197130f4520SKenneth D. Merry 	case CTL_SS_NEED_MORE_SPACE:
198bb8f9017SAlexander Motin 		if (more_space_count >= 2)
199bb8f9017SAlexander Motin 			errx(1, "CTL_GET_*_STATS returned NEED_MORE_SPACE again");
200bb8f9017SAlexander Motin 		*alloc_items = get_stats.num_items * 5 / 4;
201bb8f9017SAlexander Motin 		free(*stats);
202bb8f9017SAlexander Motin 		*stats = NULL;
203130f4520SKenneth D. Merry 		more_space_count++;
204130f4520SKenneth D. Merry 		goto retry;
205130f4520SKenneth D. Merry 		break; /* NOTREACHED */
206130f4520SKenneth D. Merry 	default:
207bb8f9017SAlexander Motin 		errx(1, "CTL_GET_*_STATS ioctl returned unknown status %d",
208bb8f9017SAlexander Motin 		     get_stats.status);
209130f4520SKenneth D. Merry 		break;
210130f4520SKenneth D. Merry 	}
211130f4520SKenneth D. Merry 
212bb8f9017SAlexander Motin 	*num_items = get_stats.fill_len / sizeof(**stats);
213bb8f9017SAlexander Motin 	cur_time->tv_sec = get_stats.timestamp.tv_sec;
214bb8f9017SAlexander Motin 	cur_time->tv_nsec = get_stats.timestamp.tv_nsec;
215bb8f9017SAlexander Motin 	if (get_stats.flags & CTL_STATS_FLAG_TIME_VALID)
216bb8f9017SAlexander Motin 		*flags |= CTLSTAT_FLAG_TIME_VALID;
217130f4520SKenneth D. Merry 	else
218bb8f9017SAlexander Motin 		*flags &= ~CTLSTAT_FLAG_TIME_VALID;
219130f4520SKenneth D. Merry 
220130f4520SKenneth D. Merry 	return (0);
221130f4520SKenneth D. Merry }
222130f4520SKenneth D. Merry 
223130f4520SKenneth D. Merry static int
getcpu(struct ctl_cpu_stats * cpu_stats)224130f4520SKenneth D. Merry getcpu(struct ctl_cpu_stats *cpu_stats)
225130f4520SKenneth D. Merry {
226130f4520SKenneth D. Merry 	long cp_time[CPUSTATES];
227130f4520SKenneth D. Merry 	size_t cplen;
228130f4520SKenneth D. Merry 
229130f4520SKenneth D. Merry 	cplen = sizeof(cp_time);
230130f4520SKenneth D. Merry 
231130f4520SKenneth D. Merry 	if (sysctlbyname("kern.cp_time", &cp_time, &cplen, NULL, 0) == -1) {
232130f4520SKenneth D. Merry 		warn("sysctlbyname(kern.cp_time...) failed");
233130f4520SKenneth D. Merry 		return (1);
234130f4520SKenneth D. Merry 	}
235130f4520SKenneth D. Merry 
236130f4520SKenneth D. Merry 	cpu_stats->user = cp_time[CP_USER];
237130f4520SKenneth D. Merry 	cpu_stats->nice = cp_time[CP_NICE];
238130f4520SKenneth D. Merry 	cpu_stats->system = cp_time[CP_SYS];
239130f4520SKenneth D. Merry 	cpu_stats->intr = cp_time[CP_INTR];
240130f4520SKenneth D. Merry 	cpu_stats->idle = cp_time[CP_IDLE];
241130f4520SKenneth D. Merry 
242130f4520SKenneth D. Merry 	return (0);
243130f4520SKenneth D. Merry }
244130f4520SKenneth D. Merry 
245130f4520SKenneth D. Merry static void
compute_stats(struct ctl_io_stats * cur_stats,struct ctl_io_stats * prev_stats,long double etime,long double * mbsec,long double * kb_per_transfer,long double * transfers_per_second,long double * ms_per_transfer,long double * ms_per_dma,long double * dmas_per_second)246bb8f9017SAlexander Motin compute_stats(struct ctl_io_stats *cur_stats,
247bb8f9017SAlexander Motin 	      struct ctl_io_stats *prev_stats, long double etime,
248130f4520SKenneth D. Merry 	      long double *mbsec, long double *kb_per_transfer,
249130f4520SKenneth D. Merry 	      long double *transfers_per_second, long double *ms_per_transfer,
250130f4520SKenneth D. Merry 	      long double *ms_per_dma, long double *dmas_per_second)
251130f4520SKenneth D. Merry {
252130f4520SKenneth D. Merry 	uint64_t total_bytes = 0, total_operations = 0, total_dmas = 0;
253130f4520SKenneth D. Merry 	struct bintime total_time_bt, total_dma_bt;
254130f4520SKenneth D. Merry 	struct timespec total_time_ts, total_dma_ts;
255130f4520SKenneth D. Merry 	int i;
256130f4520SKenneth D. Merry 
257130f4520SKenneth D. Merry 	bzero(&total_time_bt, sizeof(total_time_bt));
258130f4520SKenneth D. Merry 	bzero(&total_dma_bt, sizeof(total_dma_bt));
259130f4520SKenneth D. Merry 	bzero(&total_time_ts, sizeof(total_time_ts));
260130f4520SKenneth D. Merry 	bzero(&total_dma_ts, sizeof(total_dma_ts));
261130f4520SKenneth D. Merry 	for (i = 0; i < CTL_STATS_NUM_TYPES; i++) {
262bb8f9017SAlexander Motin 		total_bytes += cur_stats->bytes[i];
263bb8f9017SAlexander Motin 		total_operations += cur_stats->operations[i];
264bb8f9017SAlexander Motin 		total_dmas += cur_stats->dmas[i];
265bb8f9017SAlexander Motin 		bintime_add(&total_time_bt, &cur_stats->time[i]);
266bb8f9017SAlexander Motin 		bintime_add(&total_dma_bt, &cur_stats->dma_time[i]);
267130f4520SKenneth D. Merry 		if (prev_stats != NULL) {
268bb8f9017SAlexander Motin 			total_bytes -= prev_stats->bytes[i];
269bb8f9017SAlexander Motin 			total_operations -= prev_stats->operations[i];
270bb8f9017SAlexander Motin 			total_dmas -= prev_stats->dmas[i];
271bb8f9017SAlexander Motin 			bintime_sub(&total_time_bt, &prev_stats->time[i]);
272bb8f9017SAlexander Motin 			bintime_sub(&total_dma_bt, &prev_stats->dma_time[i]);
273130f4520SKenneth D. Merry 		}
274130f4520SKenneth D. Merry 	}
275130f4520SKenneth D. Merry 
276130f4520SKenneth D. Merry 	*mbsec = total_bytes;
277130f4520SKenneth D. Merry 	*mbsec /= 1024 * 1024;
278130f4520SKenneth D. Merry 	if (etime > 0.0)
279130f4520SKenneth D. Merry 		*mbsec /= etime;
280130f4520SKenneth D. Merry 	else
281130f4520SKenneth D. Merry 		*mbsec = 0;
282130f4520SKenneth D. Merry 	*kb_per_transfer = total_bytes;
283130f4520SKenneth D. Merry 	*kb_per_transfer /= 1024;
284130f4520SKenneth D. Merry 	if (total_operations > 0)
285130f4520SKenneth D. Merry 		*kb_per_transfer /= total_operations;
286130f4520SKenneth D. Merry 	else
287130f4520SKenneth D. Merry 		*kb_per_transfer = 0;
288130f4520SKenneth D. Merry 	*transfers_per_second = total_operations;
289130f4520SKenneth D. Merry 	*dmas_per_second = total_dmas;
290130f4520SKenneth D. Merry 	if (etime > 0.0) {
291130f4520SKenneth D. Merry 		*transfers_per_second /= etime;
292130f4520SKenneth D. Merry 		*dmas_per_second /= etime;
293130f4520SKenneth D. Merry 	} else {
294130f4520SKenneth D. Merry 		*transfers_per_second = 0;
295130f4520SKenneth D. Merry 		*dmas_per_second = 0;
296130f4520SKenneth D. Merry 	}
297130f4520SKenneth D. Merry 
298130f4520SKenneth D. Merry 	bintime2timespec(&total_time_bt, &total_time_ts);
299130f4520SKenneth D. Merry 	bintime2timespec(&total_dma_bt, &total_dma_ts);
300130f4520SKenneth D. Merry 	if (total_operations > 0) {
301130f4520SKenneth D. Merry 		/*
302130f4520SKenneth D. Merry 		 * Convert the timespec to milliseconds.
303130f4520SKenneth D. Merry 		 */
304130f4520SKenneth D. Merry 		*ms_per_transfer = total_time_ts.tv_sec * 1000;
305130f4520SKenneth D. Merry 		*ms_per_transfer += total_time_ts.tv_nsec / 1000000;
306130f4520SKenneth D. Merry 		*ms_per_transfer /= total_operations;
307130f4520SKenneth D. Merry 	} else
308130f4520SKenneth D. Merry 		*ms_per_transfer = 0;
309130f4520SKenneth D. Merry 
310130f4520SKenneth D. Merry 	if (total_dmas > 0) {
311130f4520SKenneth D. Merry 		/*
312130f4520SKenneth D. Merry 		 * Convert the timespec to milliseconds.
313130f4520SKenneth D. Merry 		 */
314130f4520SKenneth D. Merry 		*ms_per_dma = total_dma_ts.tv_sec * 1000;
315130f4520SKenneth D. Merry 		*ms_per_dma += total_dma_ts.tv_nsec / 1000000;
316130f4520SKenneth D. Merry 		*ms_per_dma /= total_dmas;
317130f4520SKenneth D. Merry 	} else
318130f4520SKenneth D. Merry 		*ms_per_dma = 0;
319130f4520SKenneth D. Merry }
320130f4520SKenneth D. Merry 
321130f4520SKenneth D. Merry /* The dump_stats() and json_stats() functions perform essentially the same
322130f4520SKenneth D. Merry  * purpose, but dump the statistics in different formats.  JSON is more
323130f4520SKenneth D. Merry  * conducive to programming, however.
324130f4520SKenneth D. Merry  */
325130f4520SKenneth D. Merry 
326924233ebSAlexander Motin #define	PRINT_BINTIME(bt) \
327924233ebSAlexander Motin 	printf("%jd.%06ju", (intmax_t)(bt).sec, \
3282eb293b9SAlexander Motin 	       (uintmax_t)(((bt).frac >> 32) * 1000000 >> 32))
329bf70beceSEd Schouten static const char *iotypes[] = {"NO IO", "READ", "WRITE"};
330130f4520SKenneth D. Merry 
331130f4520SKenneth D. Merry static void
ctlstat_dump(struct ctlstat_context * ctx)332bb8f9017SAlexander Motin ctlstat_dump(struct ctlstat_context *ctx)
333bb8f9017SAlexander Motin {
3345be4479bSAlexander Motin 	int iotype, i, n;
335bb8f9017SAlexander Motin 	struct ctl_io_stats *stats = ctx->cur_stats;
336130f4520SKenneth D. Merry 
3375be4479bSAlexander Motin 	for (i = n = 0; i < ctx->cur_items;i++) {
3385be4479bSAlexander Motin 		if (F_MASK(ctx) && bit_test(ctx->item_mask,
3395be4479bSAlexander Motin 		    (int)stats[i].item) == 0)
34061639a0aSAlexander Motin 			continue;
341bb8f9017SAlexander Motin 		printf("%s %d\n", F_PORTS(ctx) ? "port" : "lun", stats[i].item);
342bb8f9017SAlexander Motin 		for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) {
343bb8f9017SAlexander Motin 			printf("  io type %d (%s)\n", iotype, iotypes[iotype]);
344130f4520SKenneth D. Merry 			printf("   bytes %ju\n", (uintmax_t)
345bb8f9017SAlexander Motin 			    stats[i].bytes[iotype]);
346130f4520SKenneth D. Merry 			printf("   operations %ju\n", (uintmax_t)
347bb8f9017SAlexander Motin 			    stats[i].operations[iotype]);
348bb8f9017SAlexander Motin 			printf("   dmas %ju\n", (uintmax_t)
349bb8f9017SAlexander Motin 			    stats[i].dmas[iotype]);
350924233ebSAlexander Motin 			printf("   io time ");
351924233ebSAlexander Motin 			PRINT_BINTIME(stats[i].time[iotype]);
352924233ebSAlexander Motin 			printf("\n   dma time ");
353924233ebSAlexander Motin 			PRINT_BINTIME(stats[i].dma_time[iotype]);
354924233ebSAlexander Motin 			printf("\n");
355130f4520SKenneth D. Merry 		}
3565be4479bSAlexander Motin 		if (++n >= ctx->numdevs)
3575be4479bSAlexander Motin 			break;
358130f4520SKenneth D. Merry 	}
359130f4520SKenneth D. Merry }
360130f4520SKenneth D. Merry 
361130f4520SKenneth D. Merry static void
ctlstat_json(struct ctlstat_context * ctx)362130f4520SKenneth D. Merry ctlstat_json(struct ctlstat_context *ctx) {
3635be4479bSAlexander Motin 	int iotype, i, n;
364bb8f9017SAlexander Motin 	struct ctl_io_stats *stats = ctx->cur_stats;
365130f4520SKenneth D. Merry 
366bb8f9017SAlexander Motin 	printf("{\"%s\":[", F_PORTS(ctx) ? "ports" : "luns");
3675be4479bSAlexander Motin 	for (i = n = 0; i < ctx->cur_items; i++) {
3685be4479bSAlexander Motin 		if (F_MASK(ctx) && bit_test(ctx->item_mask,
3695be4479bSAlexander Motin 		    (int)stats[i].item) == 0)
37061639a0aSAlexander Motin 			continue;
371130f4520SKenneth D. Merry 		printf("{\"num\":%d,\"io\":[",
372bb8f9017SAlexander Motin 		    stats[i].item);
373bb8f9017SAlexander Motin 		for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) {
374130f4520SKenneth D. Merry 			printf("{\"type\":\"%s\",", iotypes[iotype]);
375924233ebSAlexander Motin 			printf("\"bytes\":%ju,", (uintmax_t)
376924233ebSAlexander Motin 			    stats[i].bytes[iotype]);
377924233ebSAlexander Motin 			printf("\"operations\":%ju,", (uintmax_t)
378924233ebSAlexander Motin 			    stats[i].operations[iotype]);
379924233ebSAlexander Motin 			printf("\"dmas\":%ju,", (uintmax_t)
380bb8f9017SAlexander Motin 			    stats[i].dmas[iotype]);
381924233ebSAlexander Motin 			printf("\"io time\":");
382924233ebSAlexander Motin 			PRINT_BINTIME(stats[i].time[iotype]);
383924233ebSAlexander Motin 			printf(",\"dma time\":");
384924233ebSAlexander Motin 			PRINT_BINTIME(stats[i].dma_time[iotype]);
385924233ebSAlexander Motin 			printf("}");
386130f4520SKenneth D. Merry 			if (iotype < (CTL_STATS_NUM_TYPES - 1))
387130f4520SKenneth D. Merry 				printf(","); /* continue io array */
388130f4520SKenneth D. Merry 		}
389bb8f9017SAlexander Motin 		printf("]}");
3905be4479bSAlexander Motin 		if (++n >= ctx->numdevs)
3915be4479bSAlexander Motin 			break;
392bb8f9017SAlexander Motin 		if (i < (ctx->cur_items - 1))
393130f4520SKenneth D. Merry 			printf(","); /* continue lun array */
394130f4520SKenneth D. Merry 	}
395bb8f9017SAlexander Motin 	printf("]}");
396130f4520SKenneth D. Merry }
397130f4520SKenneth D. Merry 
3984c163a54SAlan Somers #define CTLSTAT_PROMETHEUS_LOOP(field, collector) \
3991a7f22d9SAlan Somers 	for (i = n = 0; i < ctx->cur_items; i++) { \
4001a7f22d9SAlan Somers 		if (F_MASK(ctx) && bit_test(ctx->item_mask, \
4011a7f22d9SAlan Somers 		    (int)stats[i].item) == 0) \
4021a7f22d9SAlan Somers 			continue; \
4031a7f22d9SAlan Somers 		for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { \
4044c163a54SAlan Somers 			int idx = stats[i].item; \
4054c163a54SAlan Somers 			/* \
4064c163a54SAlan Somers 			 * Note that Prometheus considers a label value of "" \
4074c163a54SAlan Somers 			 * to be the same as no label at all \
4084c163a54SAlan Somers 			 */ \
4094c163a54SAlan Somers 			const char *target = ""; \
4104c163a54SAlan Somers 			if (strcmp(collector, "port") == 0 && \
4114c163a54SAlan Somers 				targdata.targets[idx] != NULL) \
4124c163a54SAlan Somers 			{ \
4134c163a54SAlan Somers 				target = targdata.targets[idx]; \
4144c163a54SAlan Somers 			} \
4154c163a54SAlan Somers 			printf("iscsi_%s_" #field "{" \
4164c163a54SAlan Somers 			    "%s=\"%u\",target=\"%s\",type=\"%s\"} %" PRIu64 \
4171a7f22d9SAlan Somers 			    "\n", \
4184c163a54SAlan Somers 			    collector, collector, \
4194c163a54SAlan Somers 			    idx, target, iotypes[iotype], \
4201a7f22d9SAlan Somers 			    stats[i].field[iotype]); \
4211a7f22d9SAlan Somers 		} \
4221a7f22d9SAlan Somers 	} \
4231a7f22d9SAlan Somers 
4244c163a54SAlan Somers #define CTLSTAT_PROMETHEUS_TIMELOOP(field, collector) \
4251a7f22d9SAlan Somers 	for (i = n = 0; i < ctx->cur_items; i++) { \
4261a7f22d9SAlan Somers 		if (F_MASK(ctx) && bit_test(ctx->item_mask, \
4271a7f22d9SAlan Somers 		    (int)stats[i].item) == 0) \
4281a7f22d9SAlan Somers 			continue; \
4291a7f22d9SAlan Somers 		for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { \
4301a7f22d9SAlan Somers 			uint64_t us; \
4311a7f22d9SAlan Somers 			struct timespec ts; \
4324c163a54SAlan Somers 			int idx = stats[i].item; \
4334c163a54SAlan Somers 			/* \
4344c163a54SAlan Somers 			 * Note that Prometheus considers a label value of "" \
4354c163a54SAlan Somers 			 * to be the same as no label at all \
4364c163a54SAlan Somers 			 */ \
4374c163a54SAlan Somers 			const char *target = ""; \
4384c163a54SAlan Somers 			if (strcmp(collector, "port") == 0 && \
4394c163a54SAlan Somers 				targdata.targets[idx] != NULL) \
4404c163a54SAlan Somers 			{ \
4414c163a54SAlan Somers 				target = targdata.targets[idx]; \
4424c163a54SAlan Somers 			} \
4431a7f22d9SAlan Somers 			bintime2timespec(&stats[i].field[iotype], &ts); \
4441a7f22d9SAlan Somers 			us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000; \
4454c163a54SAlan Somers 			printf("iscsi_%s_" #field "{" \
4464c163a54SAlan Somers 			    "%s=\"%u\",target=\"%s\",type=\"%s\"} %" PRIu64 \
4471a7f22d9SAlan Somers 			    "\n", \
4484c163a54SAlan Somers 			    collector, collector, \
4494c163a54SAlan Somers 			    idx, target, iotypes[iotype], us); \
4501a7f22d9SAlan Somers 		} \
4511a7f22d9SAlan Somers 	} \
4521a7f22d9SAlan Somers 
4531a7f22d9SAlan Somers static void
cctl_start_pelement(void * user_data,const char * name,const char ** attr)4544c163a54SAlan Somers cctl_start_pelement(void *user_data, const char *name, const char **attr)
4551a7f22d9SAlan Somers {
4561a7f22d9SAlan Somers 	struct cctl_portlist_data* targdata = user_data;
4571a7f22d9SAlan Somers 
4581a7f22d9SAlan Somers 	targdata->level++;
4591a7f22d9SAlan Somers 	if ((u_int)targdata->level >= (sizeof(targdata->cur_sb) /
4601a7f22d9SAlan Somers 	    sizeof(targdata->cur_sb[0])))
4611a7f22d9SAlan Somers 		errx(1, "%s: too many nesting levels, %zd max", __func__,
4621a7f22d9SAlan Somers 		     sizeof(targdata->cur_sb) / sizeof(targdata->cur_sb[0]));
4631a7f22d9SAlan Somers 
4641a7f22d9SAlan Somers 	targdata->cur_sb[targdata->level] = sbuf_new_auto();
4651a7f22d9SAlan Somers 	if (targdata->cur_sb[targdata->level] == NULL)
4661a7f22d9SAlan Somers 		err(1, "%s: Unable to allocate sbuf", __func__);
4671a7f22d9SAlan Somers 
4681a7f22d9SAlan Somers 	if (strcmp(name, "targ_port") == 0) {
4694c163a54SAlan Somers 		int i = 0;
4704c163a54SAlan Somers 
4711a7f22d9SAlan Somers 		targdata->lun = -1;
4724c163a54SAlan Somers 		targdata->id = -1;
4731a7f22d9SAlan Somers 		free(targdata->target);
4741a7f22d9SAlan Somers 		targdata->target = NULL;
4754c163a54SAlan Somers 		while (attr[i]) {
4764c163a54SAlan Somers 			if (strcmp(attr[i], "id") == 0) {
4774c163a54SAlan Somers 				/*
4784c163a54SAlan Somers 				 * Well-formed XML always pairs keys with
4794c163a54SAlan Somers 				 * values in attr
4804c163a54SAlan Somers 				 */
4814c163a54SAlan Somers 				assert(attr[i + 1]);
4824c163a54SAlan Somers 				targdata->id = atoi(attr[i + 1]);
4834c163a54SAlan Somers 			}
4844c163a54SAlan Somers 			i += 2;
4854c163a54SAlan Somers 		}
4864c163a54SAlan Somers 
4871a7f22d9SAlan Somers 	}
4881a7f22d9SAlan Somers }
4891a7f22d9SAlan Somers 
4901a7f22d9SAlan Somers static void
cctl_char_phandler(void * user_data,const XML_Char * str,int len)4911a7f22d9SAlan Somers cctl_char_phandler(void *user_data, const XML_Char *str, int len)
4921a7f22d9SAlan Somers {
4931a7f22d9SAlan Somers 	struct cctl_portlist_data *targdata = user_data;
4941a7f22d9SAlan Somers 
4951a7f22d9SAlan Somers 	sbuf_bcat(targdata->cur_sb[targdata->level], str, len);
4961a7f22d9SAlan Somers }
4971a7f22d9SAlan Somers 
4981a7f22d9SAlan Somers static void
cctl_end_pelement(void * user_data,const char * name)4991a7f22d9SAlan Somers cctl_end_pelement(void *user_data, const char *name)
5001a7f22d9SAlan Somers {
5011a7f22d9SAlan Somers 	struct cctl_portlist_data* targdata = user_data;
5021a7f22d9SAlan Somers 	char *str;
5031a7f22d9SAlan Somers 
5041a7f22d9SAlan Somers 	if (targdata->cur_sb[targdata->level] == NULL)
5051a7f22d9SAlan Somers 		errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
5061a7f22d9SAlan Somers 		     targdata->level, name);
5071a7f22d9SAlan Somers 
5081a7f22d9SAlan Somers 	if (sbuf_finish(targdata->cur_sb[targdata->level]) != 0)
5091a7f22d9SAlan Somers 		err(1, "%s: sbuf_finish", __func__);
5101a7f22d9SAlan Somers 	str = strdup(sbuf_data(targdata->cur_sb[targdata->level]));
5111a7f22d9SAlan Somers 	if (str == NULL)
5121a7f22d9SAlan Somers 		err(1, "%s can't allocate %zd bytes for string", __func__,
5131a7f22d9SAlan Somers 		    sbuf_len(targdata->cur_sb[targdata->level]));
5141a7f22d9SAlan Somers 
5151a7f22d9SAlan Somers 	sbuf_delete(targdata->cur_sb[targdata->level]);
5161a7f22d9SAlan Somers 	targdata->cur_sb[targdata->level] = NULL;
5171a7f22d9SAlan Somers 	targdata->level--;
5181a7f22d9SAlan Somers 
5191a7f22d9SAlan Somers 	if (strcmp(name, "target") == 0) {
5201a7f22d9SAlan Somers 		free(targdata->target);
5211a7f22d9SAlan Somers 		targdata->target = str;
5221a7f22d9SAlan Somers 	} else if (strcmp(name, "targ_port") == 0) {
5234c163a54SAlan Somers 		if (targdata->id >= 0 && targdata->target != NULL) {
5244c163a54SAlan Somers 			if (targdata->id >= targdata->ntargets) {
5251a7f22d9SAlan Somers 				/*
5261a7f22d9SAlan Somers 				 * This can happen for example if there are
5274c163a54SAlan Somers 				 * targets with no LUNs.
5281a7f22d9SAlan Somers 				 */
5291a7f22d9SAlan Somers 				targdata->ntargets = MAX(targdata->ntargets * 2,
5304c163a54SAlan Somers 					targdata->id + 1);
5311a7f22d9SAlan Somers 				size_t newsize = targdata->ntargets *
5321a7f22d9SAlan Somers 					sizeof(char*);
5331a7f22d9SAlan Somers 				targdata->targets = rallocx(targdata->targets,
5341a7f22d9SAlan Somers 					newsize, MALLOCX_ZERO);
5351a7f22d9SAlan Somers 			}
5364c163a54SAlan Somers 			free(targdata->targets[targdata->id]);
5374c163a54SAlan Somers 			targdata->targets[targdata->id] = targdata->target;
5381a7f22d9SAlan Somers 			targdata->target = NULL;
5391a7f22d9SAlan Somers 		}
5401a7f22d9SAlan Somers 		free(str);
5411a7f22d9SAlan Somers 	} else {
5421a7f22d9SAlan Somers 		free(str);
5431a7f22d9SAlan Somers 	}
5441a7f22d9SAlan Somers }
5451a7f22d9SAlan Somers 
5461a7f22d9SAlan Somers static void
ctlstat_prometheus(int fd,struct ctlstat_context * ctx,bool ports)5474c163a54SAlan Somers ctlstat_prometheus(int fd, struct ctlstat_context *ctx, bool ports) {
5481a7f22d9SAlan Somers 	struct ctl_io_stats *stats = ctx->cur_stats;
5491a7f22d9SAlan Somers 	struct ctl_lun_list list;
5501a7f22d9SAlan Somers 	struct cctl_portlist_data targdata;
5511a7f22d9SAlan Somers 	XML_Parser parser;
5521a7f22d9SAlan Somers 	char *port_str = NULL;
5531a7f22d9SAlan Somers 	int iotype, i, n, retval;
5541a7f22d9SAlan Somers 	int port_len = 4096;
5554c163a54SAlan Somers 	const char *collector;
5561a7f22d9SAlan Somers 
5571a7f22d9SAlan Somers 	bzero(&targdata, sizeof(targdata));
5581a7f22d9SAlan Somers 	targdata.ntargets = ctx->cur_items;
5591a7f22d9SAlan Somers 	targdata.targets = calloc(targdata.ntargets, sizeof(char*));
5601a7f22d9SAlan Somers retry:
5611a7f22d9SAlan Somers 	port_str = (char *)realloc(port_str, port_len);
5621a7f22d9SAlan Somers 	bzero(&list, sizeof(list));
5631a7f22d9SAlan Somers 	list.alloc_len = port_len;
5641a7f22d9SAlan Somers 	list.status = CTL_LUN_LIST_NONE;
5651a7f22d9SAlan Somers 	list.lun_xml = port_str;
5661a7f22d9SAlan Somers 	if (ioctl(fd, CTL_PORT_LIST, &list) == -1)
5671a7f22d9SAlan Somers 		err(1, "%s: error issuing CTL_PORT_LIST ioctl", __func__);
5681a7f22d9SAlan Somers 	if (list.status == CTL_LUN_LIST_ERROR) {
5691a7f22d9SAlan Somers 		warnx("%s: error returned from CTL_PORT_LIST ioctl:\n%s",
5701a7f22d9SAlan Somers 		      __func__, list.error_str);
5711a7f22d9SAlan Somers 	} else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
5721a7f22d9SAlan Somers 		port_len <<= 1;
5731a7f22d9SAlan Somers 		goto retry;
5741a7f22d9SAlan Somers 	}
5751a7f22d9SAlan Somers 
5761a7f22d9SAlan Somers 	parser = XML_ParserCreate(NULL);
5771a7f22d9SAlan Somers 	if (parser == NULL)
5781a7f22d9SAlan Somers 		err(1, "%s: Unable to create XML parser", __func__);
5791a7f22d9SAlan Somers 	XML_SetUserData(parser, &targdata);
5801a7f22d9SAlan Somers 	XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement);
5811a7f22d9SAlan Somers 	XML_SetCharacterDataHandler(parser, cctl_char_phandler);
5821a7f22d9SAlan Somers 
5831a7f22d9SAlan Somers 	retval = XML_Parse(parser, port_str, strlen(port_str), 1);
5841a7f22d9SAlan Somers 	if (retval != 1) {
5851a7f22d9SAlan Somers 		errx(1, "%s: Unable to parse XML: Error %d", __func__,
5861a7f22d9SAlan Somers 		    XML_GetErrorCode(parser));
5871a7f22d9SAlan Somers 	}
5881a7f22d9SAlan Somers 	XML_ParserFree(parser);
5891a7f22d9SAlan Somers 
5904c163a54SAlan Somers 	collector = ports ? "port" : "lun";
5911a7f22d9SAlan Somers 
5924c163a54SAlan Somers 	printf("# HELP iscsi_%s_bytes Number of bytes\n"
5934c163a54SAlan Somers 	       "# TYPE iscsi_%s_bytes counter\n", collector, collector);
5944c163a54SAlan Somers 	CTLSTAT_PROMETHEUS_LOOP(bytes, collector);
5954c163a54SAlan Somers 	printf("# HELP iscsi_%s_dmas Number of DMA\n"
5964c163a54SAlan Somers 	       "# TYPE iscsi_%s_dmas counter\n", collector, collector);
5974c163a54SAlan Somers 	CTLSTAT_PROMETHEUS_LOOP(dmas, collector);
5984c163a54SAlan Somers 	printf("# HELP iscsi_%s_operations Number of operations\n"
5994c163a54SAlan Somers 	       "# TYPE iscsi_%s_operations counter\n", collector, collector);
6004c163a54SAlan Somers 	CTLSTAT_PROMETHEUS_LOOP(operations, collector);
6014c163a54SAlan Somers 	printf("# HELP iscsi_%s_time Cumulative operation time in us\n"
6024c163a54SAlan Somers 	       "# TYPE iscsi_%s_time counter\n", collector, collector);
6034c163a54SAlan Somers 	CTLSTAT_PROMETHEUS_TIMELOOP(time, collector);
6044c163a54SAlan Somers 	printf("# HELP iscsi_%s_dma_time Cumulative DMA time in us\n"
6054c163a54SAlan Somers 	       "# TYPE iscsi_%s_dma_time counter\n", collector, collector);
6064c163a54SAlan Somers 	CTLSTAT_PROMETHEUS_TIMELOOP(dma_time, collector);
6071a7f22d9SAlan Somers 
6081a7f22d9SAlan Somers 	for (i = 0; i < targdata.ntargets; i++)
6091a7f22d9SAlan Somers 		free(targdata.targets[i]);
6101a7f22d9SAlan Somers 	free(targdata.target);
6111a7f22d9SAlan Somers 	free(targdata.targets);
6121a7f22d9SAlan Somers 
6131a7f22d9SAlan Somers 	fflush(stdout);
6141a7f22d9SAlan Somers }
6151a7f22d9SAlan Somers 
616130f4520SKenneth D. Merry static void
ctlstat_standard(struct ctlstat_context * ctx)617130f4520SKenneth D. Merry ctlstat_standard(struct ctlstat_context *ctx) {
6185e796316SKenneth D. Merry 	long double etime;
619130f4520SKenneth D. Merry 	uint64_t delta_jiffies, delta_idle;
620130f4520SKenneth D. Merry 	long double cpu_percentage;
6215be4479bSAlexander Motin 	int i, j, n;
622130f4520SKenneth D. Merry 
623130f4520SKenneth D. Merry 	cpu_percentage = 0;
624130f4520SKenneth D. Merry 
625130f4520SKenneth D. Merry 	if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0))
626130f4520SKenneth D. Merry 		errx(1, "error returned from getcpu()");
627130f4520SKenneth D. Merry 
6285e796316SKenneth D. Merry 	etime = ctx->cur_time.tv_sec - ctx->prev_time.tv_sec +
6295e796316SKenneth D. Merry 	    (ctx->prev_time.tv_nsec - ctx->cur_time.tv_nsec) * 1e-9;
630130f4520SKenneth D. Merry 
631130f4520SKenneth D. Merry 	if (F_CPU(ctx)) {
632130f4520SKenneth D. Merry 		ctx->prev_total_jiffies = ctx->cur_total_jiffies;
633130f4520SKenneth D. Merry 		ctx->cur_total_jiffies = ctx->cur_cpu.user +
634130f4520SKenneth D. Merry 		    ctx->cur_cpu.nice + ctx->cur_cpu.system +
635130f4520SKenneth D. Merry 		    ctx->cur_cpu.intr + ctx->cur_cpu.idle;
636130f4520SKenneth D. Merry 		delta_jiffies = ctx->cur_total_jiffies;
637130f4520SKenneth D. Merry 		if (F_FIRST(ctx) == 0)
638130f4520SKenneth D. Merry 			delta_jiffies -= ctx->prev_total_jiffies;
639130f4520SKenneth D. Merry 		ctx->prev_idle = ctx->cur_idle;
640130f4520SKenneth D. Merry 		ctx->cur_idle = ctx->cur_cpu.idle;
641130f4520SKenneth D. Merry 		delta_idle = ctx->cur_idle - ctx->prev_idle;
642130f4520SKenneth D. Merry 
643130f4520SKenneth D. Merry 		cpu_percentage = delta_jiffies - delta_idle;
644130f4520SKenneth D. Merry 		cpu_percentage /= delta_jiffies;
645130f4520SKenneth D. Merry 		cpu_percentage *= 100;
646130f4520SKenneth D. Merry 	}
647130f4520SKenneth D. Merry 
648130f4520SKenneth D. Merry 	if (F_HDR(ctx)) {
649130f4520SKenneth D. Merry 		ctx->header_interval--;
650130f4520SKenneth D. Merry 		if (ctx->header_interval <= 0) {
651130f4520SKenneth D. Merry 			if (F_CPU(ctx))
652130f4520SKenneth D. Merry 				fprintf(stdout, " CPU");
65361639a0aSAlexander Motin 			if (F_TOTALS(ctx)) {
65461639a0aSAlexander Motin 				fprintf(stdout, "%s     Read       %s"
65561639a0aSAlexander Motin 					"    Write       %s    Total\n",
656bb8f9017SAlexander Motin 					(F_TIMEVAL(ctx) != 0) ? "      " : "",
657bb8f9017SAlexander Motin 					(F_TIMEVAL(ctx) != 0) ? "      " : "",
658bb8f9017SAlexander Motin 					(F_TIMEVAL(ctx) != 0) ? "      " : "");
6595be4479bSAlexander Motin 				n = 3;
66061639a0aSAlexander Motin 			} else {
6615e4b2529SBaptiste Daroussin 				for (i = n = 0; i < min(ctl_stat_bits,
662bb8f9017SAlexander Motin 				     ctx->cur_items); i++) {
663bb8f9017SAlexander Motin 					int item;
664130f4520SKenneth D. Merry 
665130f4520SKenneth D. Merry 					/*
666130f4520SKenneth D. Merry 					 * Obviously this won't work with
667130f4520SKenneth D. Merry 					 * LUN numbers greater than a signed
668130f4520SKenneth D. Merry 					 * integer.
669130f4520SKenneth D. Merry 					 */
670bb8f9017SAlexander Motin 					item = (int)ctx->cur_stats[i].item;
671130f4520SKenneth D. Merry 
672bb8f9017SAlexander Motin 					if (F_MASK(ctx) &&
673bb8f9017SAlexander Motin 					    bit_test(ctx->item_mask, item) == 0)
674130f4520SKenneth D. Merry 						continue;
675d5a6319aSAlexander Motin 					fprintf(stdout, "%15.6s%d %s",
676bb8f9017SAlexander Motin 					    F_PORTS(ctx) ? "port" : "lun", item,
677bb8f9017SAlexander Motin 					    (F_TIMEVAL(ctx) != 0) ? "     " : "");
6785be4479bSAlexander Motin 					if (++n >= ctx->numdevs)
6795be4479bSAlexander Motin 						break;
680130f4520SKenneth D. Merry 				}
681130f4520SKenneth D. Merry 				fprintf(stdout, "\n");
682130f4520SKenneth D. Merry 			}
68361639a0aSAlexander Motin 			if (F_CPU(ctx))
68461639a0aSAlexander Motin 				fprintf(stdout, "    ");
6855be4479bSAlexander Motin 			for (i = 0; i < n; i++)
68661639a0aSAlexander Motin 				fprintf(stdout, "%s KB/t   %s MB/s",
687bb8f9017SAlexander Motin 					(F_TIMEVAL(ctx) != 0) ? "    ms" : "",
688130f4520SKenneth D. Merry 					(F_DMA(ctx) == 0) ? "tps" : "dps");
689130f4520SKenneth D. Merry 			fprintf(stdout, "\n");
690130f4520SKenneth D. Merry 			ctx->header_interval = 20;
691130f4520SKenneth D. Merry 		}
692130f4520SKenneth D. Merry 	}
693130f4520SKenneth D. Merry 
69461639a0aSAlexander Motin 	if (F_CPU(ctx))
69561639a0aSAlexander Motin 		fprintf(stdout, "%3.0Lf%%", cpu_percentage);
696130f4520SKenneth D. Merry 	if (F_TOTALS(ctx) != 0) {
697130f4520SKenneth D. Merry 		long double mbsec[3];
698130f4520SKenneth D. Merry 		long double kb_per_transfer[3];
699130f4520SKenneth D. Merry 		long double transfers_per_sec[3];
700130f4520SKenneth D. Merry 		long double ms_per_transfer[3];
701130f4520SKenneth D. Merry 		long double ms_per_dma[3];
702130f4520SKenneth D. Merry 		long double dmas_per_sec[3];
703130f4520SKenneth D. Merry 
704130f4520SKenneth D. Merry 		for (i = 0; i < 3; i++)
705130f4520SKenneth D. Merry 			ctx->prev_total_stats[i] = ctx->cur_total_stats[i];
706130f4520SKenneth D. Merry 
707130f4520SKenneth D. Merry 		memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats));
708130f4520SKenneth D. Merry 
709130f4520SKenneth D. Merry 		/* Use macros to make the next loop more readable. */
710bb8f9017SAlexander Motin #define	ADD_STATS_BYTES(st, i, j) \
711bb8f9017SAlexander Motin 	ctx->cur_total_stats[st].bytes[j] += \
712bb8f9017SAlexander Motin 	    ctx->cur_stats[i].bytes[j]
713bb8f9017SAlexander Motin #define	ADD_STATS_OPERATIONS(st, i, j) \
714bb8f9017SAlexander Motin 	ctx->cur_total_stats[st].operations[j] += \
715bb8f9017SAlexander Motin 	    ctx->cur_stats[i].operations[j]
716bb8f9017SAlexander Motin #define	ADD_STATS_DMAS(st, i, j) \
717bb8f9017SAlexander Motin 	ctx->cur_total_stats[st].dmas[j] += \
718bb8f9017SAlexander Motin 	    ctx->cur_stats[i].dmas[j]
719bb8f9017SAlexander Motin #define	ADD_STATS_TIME(st, i, j) \
720bb8f9017SAlexander Motin 	bintime_add(&ctx->cur_total_stats[st].time[j], \
721bb8f9017SAlexander Motin 	    &ctx->cur_stats[i].time[j])
722bb8f9017SAlexander Motin #define	ADD_STATS_DMA_TIME(st, i, j) \
723bb8f9017SAlexander Motin 	bintime_add(&ctx->cur_total_stats[st].dma_time[j], \
724bb8f9017SAlexander Motin 	    &ctx->cur_stats[i].dma_time[j])
725130f4520SKenneth D. Merry 
726bb8f9017SAlexander Motin 		for (i = 0; i < ctx->cur_items; i++) {
727bb8f9017SAlexander Motin 			if (F_MASK(ctx) && bit_test(ctx->item_mask,
728bb8f9017SAlexander Motin 			    (int)ctx->cur_stats[i].item) == 0)
72961639a0aSAlexander Motin 				continue;
730130f4520SKenneth D. Merry 			for (j = 0; j < CTL_STATS_NUM_TYPES; j++) {
731bb8f9017SAlexander Motin 				ADD_STATS_BYTES(2, i, j);
732bb8f9017SAlexander Motin 				ADD_STATS_OPERATIONS(2, i, j);
733bb8f9017SAlexander Motin 				ADD_STATS_DMAS(2, i, j);
734bb8f9017SAlexander Motin 				ADD_STATS_TIME(2, i, j);
735bb8f9017SAlexander Motin 				ADD_STATS_DMA_TIME(2, i, j);
736130f4520SKenneth D. Merry 			}
737bb8f9017SAlexander Motin 			ADD_STATS_BYTES(0, i, CTL_STATS_READ);
738bb8f9017SAlexander Motin 			ADD_STATS_OPERATIONS(0, i, CTL_STATS_READ);
739bb8f9017SAlexander Motin 			ADD_STATS_DMAS(0, i, CTL_STATS_READ);
740bb8f9017SAlexander Motin 			ADD_STATS_TIME(0, i, CTL_STATS_READ);
741bb8f9017SAlexander Motin 			ADD_STATS_DMA_TIME(0, i, CTL_STATS_READ);
742130f4520SKenneth D. Merry 
743bb8f9017SAlexander Motin 			ADD_STATS_BYTES(1, i, CTL_STATS_WRITE);
744bb8f9017SAlexander Motin 			ADD_STATS_OPERATIONS(1, i, CTL_STATS_WRITE);
745bb8f9017SAlexander Motin 			ADD_STATS_DMAS(1, i, CTL_STATS_WRITE);
746bb8f9017SAlexander Motin 			ADD_STATS_TIME(1, i, CTL_STATS_WRITE);
747bb8f9017SAlexander Motin 			ADD_STATS_DMA_TIME(1, i, CTL_STATS_WRITE);
748130f4520SKenneth D. Merry 		}
749130f4520SKenneth D. Merry 
750130f4520SKenneth D. Merry 		for (i = 0; i < 3; i++) {
751bb8f9017SAlexander Motin 			compute_stats(&ctx->cur_total_stats[i],
752130f4520SKenneth D. Merry 				F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i],
753130f4520SKenneth D. Merry 				etime, &mbsec[i], &kb_per_transfer[i],
754130f4520SKenneth D. Merry 				&transfers_per_sec[i],
755130f4520SKenneth D. Merry 				&ms_per_transfer[i], &ms_per_dma[i],
756130f4520SKenneth D. Merry 				&dmas_per_sec[i]);
757130f4520SKenneth D. Merry 			if (F_DMA(ctx) != 0)
75861639a0aSAlexander Motin 				fprintf(stdout, " %5.1Lf",
759130f4520SKenneth D. Merry 					ms_per_dma[i]);
760bb8f9017SAlexander Motin 			else if (F_TIMEVAL(ctx) != 0)
76161639a0aSAlexander Motin 				fprintf(stdout, " %5.1Lf",
762130f4520SKenneth D. Merry 					ms_per_transfer[i]);
76361639a0aSAlexander Motin 			fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf",
764130f4520SKenneth D. Merry 				kb_per_transfer[i],
765130f4520SKenneth D. Merry 				(F_DMA(ctx) == 0) ? transfers_per_sec[i] :
766130f4520SKenneth D. Merry 				dmas_per_sec[i], mbsec[i]);
767130f4520SKenneth D. Merry 		}
768130f4520SKenneth D. Merry 	} else {
7695e4b2529SBaptiste Daroussin 		for (i = n = 0; i < min(ctl_stat_bits, ctx->cur_items); i++) {
770130f4520SKenneth D. Merry 			long double mbsec, kb_per_transfer;
771130f4520SKenneth D. Merry 			long double transfers_per_sec;
772130f4520SKenneth D. Merry 			long double ms_per_transfer;
773130f4520SKenneth D. Merry 			long double ms_per_dma;
774130f4520SKenneth D. Merry 			long double dmas_per_sec;
775130f4520SKenneth D. Merry 
776bb8f9017SAlexander Motin 			if (F_MASK(ctx) && bit_test(ctx->item_mask,
777bb8f9017SAlexander Motin 			    (int)ctx->cur_stats[i].item) == 0)
778130f4520SKenneth D. Merry 				continue;
779bb8f9017SAlexander Motin 			for (j = 0; j < ctx->prev_items; j++) {
780bb8f9017SAlexander Motin 				if (ctx->prev_stats[j].item ==
781bb8f9017SAlexander Motin 				    ctx->cur_stats[i].item)
782bb8f9017SAlexander Motin 					break;
783bb8f9017SAlexander Motin 			}
784bb8f9017SAlexander Motin 			if (j >= ctx->prev_items)
785bb8f9017SAlexander Motin 				j = -1;
786bb8f9017SAlexander Motin 			compute_stats(&ctx->cur_stats[i],
787bb8f9017SAlexander Motin 			    j >= 0 ? &ctx->prev_stats[j] : NULL,
78861639a0aSAlexander Motin 			    etime, &mbsec, &kb_per_transfer,
789130f4520SKenneth D. Merry 			    &transfers_per_sec, &ms_per_transfer,
790130f4520SKenneth D. Merry 			    &ms_per_dma, &dmas_per_sec);
791130f4520SKenneth D. Merry 			if (F_DMA(ctx))
79261639a0aSAlexander Motin 				fprintf(stdout, " %5.1Lf",
793130f4520SKenneth D. Merry 					ms_per_dma);
794bb8f9017SAlexander Motin 			else if (F_TIMEVAL(ctx) != 0)
79561639a0aSAlexander Motin 				fprintf(stdout, " %5.1Lf",
796130f4520SKenneth D. Merry 					ms_per_transfer);
79761639a0aSAlexander Motin 			fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf",
798130f4520SKenneth D. Merry 				kb_per_transfer, (F_DMA(ctx) == 0) ?
799130f4520SKenneth D. Merry 				transfers_per_sec : dmas_per_sec, mbsec);
8005be4479bSAlexander Motin 			if (++n >= ctx->numdevs)
8015be4479bSAlexander Motin 				break;
802130f4520SKenneth D. Merry 		}
803130f4520SKenneth D. Merry 	}
804130f4520SKenneth D. Merry }
805130f4520SKenneth D. Merry 
8064c163a54SAlan Somers static void
get_and_print_stats(int fd,struct ctlstat_context * ctx,bool ports)8074c163a54SAlan Somers get_and_print_stats(int fd, struct ctlstat_context *ctx, bool ports)
8084c163a54SAlan Somers {
8094c163a54SAlan Somers 	struct ctl_io_stats *tmp_stats;
8104c163a54SAlan Somers 	int c;
8114c163a54SAlan Somers 
8124c163a54SAlan Somers 	tmp_stats = ctx->prev_stats;
8134c163a54SAlan Somers 	ctx->prev_stats = ctx->cur_stats;
8144c163a54SAlan Somers 	ctx->cur_stats = tmp_stats;
8154c163a54SAlan Somers 	c = ctx->prev_alloc;
8164c163a54SAlan Somers 	ctx->prev_alloc = ctx->cur_alloc;
8174c163a54SAlan Somers 	ctx->cur_alloc = c;
8184c163a54SAlan Somers 	c = ctx->prev_items;
8194c163a54SAlan Somers 	ctx->prev_items = ctx->cur_items;
8204c163a54SAlan Somers 	ctx->cur_items = c;
8214c163a54SAlan Somers 	ctx->prev_time = ctx->cur_time;
8224c163a54SAlan Somers 	ctx->prev_cpu = ctx->cur_cpu;
8234c163a54SAlan Somers 	if (getstats(fd, &ctx->cur_alloc, &ctx->cur_items,
8244c163a54SAlan Somers 	    &ctx->cur_stats, &ctx->cur_time, &ctx->flags, ports) != 0)
8254c163a54SAlan Somers 		errx(1, "error returned from getstats()");
8264c163a54SAlan Somers 
8274c163a54SAlan Somers 	switch(ctx->mode) {
8284c163a54SAlan Somers 	case CTLSTAT_MODE_STANDARD:
8294c163a54SAlan Somers 		ctlstat_standard(ctx);
8304c163a54SAlan Somers 		break;
8314c163a54SAlan Somers 	case CTLSTAT_MODE_DUMP:
8324c163a54SAlan Somers 		ctlstat_dump(ctx);
8334c163a54SAlan Somers 		break;
8344c163a54SAlan Somers 	case CTLSTAT_MODE_JSON:
8354c163a54SAlan Somers 		ctlstat_json(ctx);
8364c163a54SAlan Somers 		break;
8374c163a54SAlan Somers 	case CTLSTAT_MODE_PROMETHEUS:
8384c163a54SAlan Somers 		ctlstat_prometheus(fd, ctx, ports);
8394c163a54SAlan Somers 		break;
8404c163a54SAlan Somers 	default:
8414c163a54SAlan Somers 		break;
8424c163a54SAlan Somers 	}
8434c163a54SAlan Somers }
8444c163a54SAlan Somers 
845130f4520SKenneth D. Merry int
main(int argc,char ** argv)846130f4520SKenneth D. Merry main(int argc, char **argv)
847130f4520SKenneth D. Merry {
848130f4520SKenneth D. Merry 	int c;
849130f4520SKenneth D. Merry 	int count, waittime;
850130f4520SKenneth D. Merry 	int fd, retval;
8515e4b2529SBaptiste Daroussin 	size_t size;
852130f4520SKenneth D. Merry 	struct ctlstat_context ctx;
853130f4520SKenneth D. Merry 
854130f4520SKenneth D. Merry 	/* default values */
855130f4520SKenneth D. Merry 	retval = 0;
856130f4520SKenneth D. Merry 	waittime = 1;
857130f4520SKenneth D. Merry 	count = -1;
858130f4520SKenneth D. Merry 	memset(&ctx, 0, sizeof(ctx));
859130f4520SKenneth D. Merry 	ctx.numdevs = 3;
860130f4520SKenneth D. Merry 	ctx.mode = CTLSTAT_MODE_STANDARD;
861130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_CPU;
862130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_FIRST_RUN;
863130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_HEADER;
864130f4520SKenneth D. Merry 
8655e4b2529SBaptiste Daroussin 	size = sizeof(ctl_stat_bits);
8665e4b2529SBaptiste Daroussin 	if (sysctlbyname("kern.cam.ctl.max_luns", &ctl_stat_bits, &size, NULL,
8675e4b2529SBaptiste Daroussin 	    0) == -1) {
8685e4b2529SBaptiste Daroussin 		/* Backward compatibility for where the sysctl wasn't exposed */
8695e4b2529SBaptiste Daroussin 		ctl_stat_bits = 1024;
8705e4b2529SBaptiste Daroussin 	}
8715e4b2529SBaptiste Daroussin 	ctx.item_mask = bit_alloc(ctl_stat_bits);
8725e4b2529SBaptiste Daroussin 	if (ctx.item_mask == NULL)
8735e4b2529SBaptiste Daroussin 		err(1, "bit_alloc() failed");
8745e4b2529SBaptiste Daroussin 
875130f4520SKenneth D. Merry 	while ((c = getopt(argc, argv, ctlstat_opts)) != -1) {
876130f4520SKenneth D. Merry 		switch (c) {
877130f4520SKenneth D. Merry 		case 'C':
878130f4520SKenneth D. Merry 			ctx.flags &= ~CTLSTAT_FLAG_CPU;
879130f4520SKenneth D. Merry 			break;
880130f4520SKenneth D. Merry 		case 'c':
881130f4520SKenneth D. Merry 			count = atoi(optarg);
882130f4520SKenneth D. Merry 			break;
883130f4520SKenneth D. Merry 		case 'd':
884130f4520SKenneth D. Merry 			ctx.flags |= CTLSTAT_FLAG_DMA_TIME;
885130f4520SKenneth D. Merry 			break;
886130f4520SKenneth D. Merry 		case 'D':
887130f4520SKenneth D. Merry 			ctx.mode = CTLSTAT_MODE_DUMP;
888130f4520SKenneth D. Merry 			waittime = 30;
889130f4520SKenneth D. Merry 			break;
890130f4520SKenneth D. Merry 		case 'h':
891130f4520SKenneth D. Merry 			ctx.flags &= ~CTLSTAT_FLAG_HEADER;
892130f4520SKenneth D. Merry 			break;
893130f4520SKenneth D. Merry 		case 'j':
894130f4520SKenneth D. Merry 			ctx.mode = CTLSTAT_MODE_JSON;
895130f4520SKenneth D. Merry 			waittime = 30;
896130f4520SKenneth D. Merry 			break;
897130f4520SKenneth D. Merry 		case 'l': {
898130f4520SKenneth D. Merry 			int cur_lun;
899130f4520SKenneth D. Merry 
900130f4520SKenneth D. Merry 			cur_lun = atoi(optarg);
9015e4b2529SBaptiste Daroussin 			if (cur_lun > ctl_stat_bits)
902130f4520SKenneth D. Merry 				errx(1, "Invalid LUN number %d", cur_lun);
903130f4520SKenneth D. Merry 
904bb8f9017SAlexander Motin 			if (!F_MASK(&ctx))
905130f4520SKenneth D. Merry 				ctx.numdevs = 1;
906130f4520SKenneth D. Merry 			else
907130f4520SKenneth D. Merry 				ctx.numdevs++;
908bb8f9017SAlexander Motin 			bit_set(ctx.item_mask, cur_lun);
909bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_MASK;
910bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_LUNS;
911130f4520SKenneth D. Merry 			break;
912130f4520SKenneth D. Merry 		}
913130f4520SKenneth D. Merry 		case 'n':
914130f4520SKenneth D. Merry 			ctx.numdevs = atoi(optarg);
915130f4520SKenneth D. Merry 			break;
91661639a0aSAlexander Motin 		case 'p': {
91761639a0aSAlexander Motin 			int cur_port;
91861639a0aSAlexander Motin 
91961639a0aSAlexander Motin 			cur_port = atoi(optarg);
9205e4b2529SBaptiste Daroussin 			if (cur_port > ctl_stat_bits)
921bb8f9017SAlexander Motin 				errx(1, "Invalid port number %d", cur_port);
92261639a0aSAlexander Motin 
923bb8f9017SAlexander Motin 			if (!F_MASK(&ctx))
924bb8f9017SAlexander Motin 				ctx.numdevs = 1;
925bb8f9017SAlexander Motin 			else
926bb8f9017SAlexander Motin 				ctx.numdevs++;
927bb8f9017SAlexander Motin 			bit_set(ctx.item_mask, cur_port);
928bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_MASK;
929bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_PORTS;
93061639a0aSAlexander Motin 			break;
93161639a0aSAlexander Motin 		}
9321a7f22d9SAlan Somers 		case 'P':
9331a7f22d9SAlan Somers 			ctx.mode = CTLSTAT_MODE_PROMETHEUS;
9341a7f22d9SAlan Somers 			break;
935130f4520SKenneth D. Merry 		case 't':
936130f4520SKenneth D. Merry 			ctx.flags |= CTLSTAT_FLAG_TOTALS;
937130f4520SKenneth D. Merry 			break;
938130f4520SKenneth D. Merry 		case 'w':
939130f4520SKenneth D. Merry 			waittime = atoi(optarg);
940130f4520SKenneth D. Merry 			break;
941130f4520SKenneth D. Merry 		default:
942130f4520SKenneth D. Merry 			retval = 1;
943130f4520SKenneth D. Merry 			usage(retval);
944130f4520SKenneth D. Merry 			exit(retval);
945130f4520SKenneth D. Merry 			break;
946130f4520SKenneth D. Merry 		}
947130f4520SKenneth D. Merry 	}
948130f4520SKenneth D. Merry 
949bb8f9017SAlexander Motin 	if (F_LUNS(&ctx) && F_PORTS(&ctx))
950bb8f9017SAlexander Motin 		errx(1, "Options -p and -l are exclusive.");
951bb8f9017SAlexander Motin 
9521a7f22d9SAlan Somers 	if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) {
9531a7f22d9SAlan Somers 		if ((count != -1) ||
9541a7f22d9SAlan Somers 			(waittime != 1) ||
9554c163a54SAlan Somers 			(F_PORTS(&ctx)) ||
9561a7f22d9SAlan Somers 			/* NB: -P could be compatible with -t in the future */
9571a7f22d9SAlan Somers 			(ctx.flags & CTLSTAT_FLAG_TOTALS))
9581a7f22d9SAlan Somers 		{
9594c163a54SAlan Somers 			errx(1, "Option -P is exclusive with -p, -c, -w, and -t");
9601a7f22d9SAlan Somers 		}
9611a7f22d9SAlan Somers 		count = 1;
9621a7f22d9SAlan Somers 	}
9631a7f22d9SAlan Somers 
964bb8f9017SAlexander Motin 	if (!F_LUNS(&ctx) && !F_PORTS(&ctx)) {
965bb8f9017SAlexander Motin 		if (F_TOTALS(&ctx))
966bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_PORTS;
967bb8f9017SAlexander Motin 		else
968bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_LUNS;
969bb8f9017SAlexander Motin 	}
970bb8f9017SAlexander Motin 
971130f4520SKenneth D. Merry 	if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1)
972130f4520SKenneth D. Merry 		err(1, "cannot open %s", CTL_DEFAULT_DEV);
973130f4520SKenneth D. Merry 
9744c163a54SAlan Somers 	if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) {
9754c163a54SAlan Somers 		/*
9764c163a54SAlan Somers 		 * NB: Some clients will print a warning if we don't set
9774c163a54SAlan Somers 		 * Content-Length, but they still work.  And the data still
9784c163a54SAlan Somers 		 * gets into Prometheus.
9794c163a54SAlan Somers 		 */
9804c163a54SAlan Somers 		printf("HTTP/1.1 200 OK\r\n"
9814c163a54SAlan Somers 		       "Connection: close\r\n"
9824c163a54SAlan Somers 		       "Content-Type: text/plain; version=0.0.4\r\n"
9834c163a54SAlan Somers 		       "\r\n");
9844c163a54SAlan Somers 	}
985130f4520SKenneth D. Merry 
9864c163a54SAlan Somers 	for (;count != 0;) {
9874c163a54SAlan Somers 		bool ports;
9884c163a54SAlan Somers 
9894c163a54SAlan Somers 		if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) {
9904c163a54SAlan Somers 			get_and_print_stats(fd, &ctx, false);
9914c163a54SAlan Somers 			get_and_print_stats(fd, &ctx, true);
9924c163a54SAlan Somers 		} else {
9934c163a54SAlan Somers 			ports = ctx.flags & CTLSTAT_FLAG_PORTS;
9944c163a54SAlan Somers 			get_and_print_stats(fd, &ctx, ports);
995130f4520SKenneth D. Merry 		}
996130f4520SKenneth D. Merry 
997130f4520SKenneth D. Merry 		fprintf(stdout, "\n");
998fcc87341SAlexander Motin 		fflush(stdout);
999130f4520SKenneth D. Merry 		ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN;
1000130f4520SKenneth D. Merry 		if (count != 1)
1001130f4520SKenneth D. Merry 			sleep(waittime);
1002130f4520SKenneth D. Merry 		if (count > 0)
1003130f4520SKenneth D. Merry 			count--;
1004130f4520SKenneth D. Merry 	}
1005130f4520SKenneth D. Merry 
1006130f4520SKenneth D. Merry 	exit (retval);
1007130f4520SKenneth D. Merry }
1008130f4520SKenneth D. Merry 
1009130f4520SKenneth D. Merry /*
1010130f4520SKenneth D. Merry  * vim: ts=8
1011130f4520SKenneth D. Merry  */
1012