xref: /freebsd/usr.bin/ctlstat/ctlstat.c (revision 1a7f22d9c211f504f6c48a86401469181a67ec34)
1130f4520SKenneth D. Merry /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
42130f4520SKenneth D. Merry __FBSDID("$FreeBSD$");
43130f4520SKenneth D. Merry 
44130f4520SKenneth D. Merry #include <sys/param.h>
45130f4520SKenneth D. Merry #include <sys/callout.h>
46*1a7f22d9SAlan Somers #include <sys/ioctl.h>
47*1a7f22d9SAlan Somers #include <sys/queue.h>
48*1a7f22d9SAlan Somers #include <sys/resource.h>
49*1a7f22d9SAlan Somers #include <sys/sbuf.h>
50*1a7f22d9SAlan Somers #include <sys/socket.h>
51*1a7f22d9SAlan Somers #include <sys/sysctl.h>
52*1a7f22d9SAlan Somers #include <sys/time.h>
53*1a7f22d9SAlan Somers #include <bsdxml.h>
54*1a7f22d9SAlan Somers #include <malloc_np.h>
55130f4520SKenneth D. Merry #include <stdint.h>
56130f4520SKenneth D. Merry #include <stdio.h>
57130f4520SKenneth D. Merry #include <stdlib.h>
58130f4520SKenneth D. Merry #include <unistd.h>
59130f4520SKenneth D. Merry #include <fcntl.h>
60*1a7f22d9SAlan Somers #include <inttypes.h>
61130f4520SKenneth D. Merry #include <getopt.h>
62130f4520SKenneth D. Merry #include <string.h>
63130f4520SKenneth D. Merry #include <errno.h>
64130f4520SKenneth D. Merry #include <err.h>
65130f4520SKenneth D. Merry #include <ctype.h>
66130f4520SKenneth D. Merry #include <bitstring.h>
67130f4520SKenneth D. Merry #include <cam/scsi/scsi_all.h>
68130f4520SKenneth D. Merry #include <cam/ctl/ctl.h>
69130f4520SKenneth D. Merry #include <cam/ctl/ctl_io.h>
70130f4520SKenneth D. Merry #include <cam/ctl/ctl_scsi_all.h>
71130f4520SKenneth D. Merry #include <cam/ctl/ctl_util.h>
72130f4520SKenneth D. Merry #include <cam/ctl/ctl_backend.h>
73130f4520SKenneth D. Merry #include <cam/ctl/ctl_ioctl.h>
74130f4520SKenneth D. Merry 
75130f4520SKenneth D. Merry /*
76bb8f9017SAlexander Motin  * The default amount of space we allocate for stats storage space.
77bb8f9017SAlexander Motin  * We dynamically allocate more if needed.
78130f4520SKenneth D. Merry  */
79bb8f9017SAlexander Motin #define	CTL_STAT_NUM_ITEMS	256
80130f4520SKenneth D. Merry 
815e4b2529SBaptiste Daroussin static int ctl_stat_bits;
82130f4520SKenneth D. Merry 
83*1a7f22d9SAlan Somers static const char *ctlstat_opts = "Cc:DPdhjl:n:p:tw:";
84*1a7f22d9SAlan Somers static const char *ctlstat_usage = "Usage:  ctlstat [-CDPdjht] [-l lunnum]"
85130f4520SKenneth D. Merry 				   "[-c count] [-n numdevs] [-w wait]\n";
86130f4520SKenneth D. Merry 
87130f4520SKenneth D. Merry struct ctl_cpu_stats {
88130f4520SKenneth D. Merry 	uint64_t user;
89130f4520SKenneth D. Merry 	uint64_t nice;
90130f4520SKenneth D. Merry 	uint64_t system;
91130f4520SKenneth D. Merry 	uint64_t intr;
92130f4520SKenneth D. Merry 	uint64_t idle;
93130f4520SKenneth D. Merry };
94130f4520SKenneth D. Merry 
95130f4520SKenneth D. Merry typedef enum {
96130f4520SKenneth D. Merry 	CTLSTAT_MODE_STANDARD,
97130f4520SKenneth D. Merry 	CTLSTAT_MODE_DUMP,
98130f4520SKenneth D. Merry 	CTLSTAT_MODE_JSON,
99*1a7f22d9SAlan Somers 	CTLSTAT_MODE_PROMETHEUS,
100130f4520SKenneth D. Merry } ctlstat_mode_types;
101130f4520SKenneth D. Merry 
102130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_CPU		(1 << 0)
103130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_HEADER		(1 << 1)
104130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_FIRST_RUN		(1 << 2)
105130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_TOTALS		(1 << 3)
106130f4520SKenneth D. Merry #define	CTLSTAT_FLAG_DMA_TIME		(1 << 4)
107bb8f9017SAlexander Motin #define	CTLSTAT_FLAG_TIME_VALID		(1 << 5)
108bb8f9017SAlexander Motin #define	CTLSTAT_FLAG_MASK		(1 << 6)
109bb8f9017SAlexander Motin #define	CTLSTAT_FLAG_LUNS		(1 << 7)
110bb8f9017SAlexander Motin #define	CTLSTAT_FLAG_PORTS		(1 << 8)
111130f4520SKenneth D. Merry #define	F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU)
112130f4520SKenneth D. Merry #define	F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER)
113130f4520SKenneth D. Merry #define	F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN)
114130f4520SKenneth D. Merry #define	F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS)
115130f4520SKenneth D. Merry #define	F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME)
116bb8f9017SAlexander Motin #define	F_TIMEVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_TIME_VALID)
117bb8f9017SAlexander Motin #define	F_MASK(ctx) ((ctx)->flags & CTLSTAT_FLAG_MASK)
118bb8f9017SAlexander Motin #define	F_LUNS(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUNS)
119bb8f9017SAlexander Motin #define	F_PORTS(ctx) ((ctx)->flags & CTLSTAT_FLAG_PORTS)
120130f4520SKenneth D. Merry 
121130f4520SKenneth D. Merry struct ctlstat_context {
122130f4520SKenneth D. Merry 	ctlstat_mode_types mode;
123130f4520SKenneth D. Merry 	int flags;
124bb8f9017SAlexander Motin 	struct ctl_io_stats *cur_stats, *prev_stats;
125bb8f9017SAlexander Motin 	struct ctl_io_stats cur_total_stats[3], prev_total_stats[3];
126130f4520SKenneth D. Merry 	struct timespec cur_time, prev_time;
127130f4520SKenneth D. Merry 	struct ctl_cpu_stats cur_cpu, prev_cpu;
128130f4520SKenneth D. Merry 	uint64_t cur_total_jiffies, prev_total_jiffies;
129130f4520SKenneth D. Merry 	uint64_t cur_idle, prev_idle;
1305e4b2529SBaptiste Daroussin 	bitstr_t *item_mask;
131bb8f9017SAlexander Motin 	int cur_items, prev_items;
132bb8f9017SAlexander Motin 	int cur_alloc, prev_alloc;
133130f4520SKenneth D. Merry 	int numdevs;
134130f4520SKenneth D. Merry 	int header_interval;
135130f4520SKenneth D. Merry };
136130f4520SKenneth D. Merry 
137*1a7f22d9SAlan Somers struct cctl_portlist_data {
138*1a7f22d9SAlan Somers 	int level;
139*1a7f22d9SAlan Somers 	struct sbuf *cur_sb[32];
140*1a7f22d9SAlan Somers 	int lun;
141*1a7f22d9SAlan Somers 	int ntargets;
142*1a7f22d9SAlan Somers 	char *target;
143*1a7f22d9SAlan Somers 	char **targets;
144*1a7f22d9SAlan Somers };
145*1a7f22d9SAlan Somers 
146130f4520SKenneth D. Merry #ifndef min
147130f4520SKenneth D. Merry #define	min(x,y)	(((x) < (y)) ? (x) : (y))
148130f4520SKenneth D. Merry #endif
149130f4520SKenneth D. Merry 
150130f4520SKenneth D. Merry static void usage(int error);
151bb8f9017SAlexander Motin static int getstats(int fd, int *alloc_items, int *num_items,
152bb8f9017SAlexander Motin     struct ctl_io_stats **xstats, struct timespec *cur_time, int *time_valid);
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
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
170bb8f9017SAlexander Motin getstats(int fd, int *alloc_items, int *num_items, struct ctl_io_stats **stats,
171130f4520SKenneth D. Merry 	 struct timespec *cur_time, int *flags)
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 
187bb8f9017SAlexander Motin 	if (ioctl(fd, (*flags & CTLSTAT_FLAG_PORTS) ? CTL_GET_PORT_STATS :
188bb8f9017SAlexander Motin 	    CTL_GET_LUN_STATS, &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
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
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
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
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 
398*1a7f22d9SAlan Somers #define CTLSTAT_PROMETHEUS_LOOP(field) \
399*1a7f22d9SAlan Somers 	for (i = n = 0; i < ctx->cur_items; i++) { \
400*1a7f22d9SAlan Somers 		if (F_MASK(ctx) && bit_test(ctx->item_mask, \
401*1a7f22d9SAlan Somers 		    (int)stats[i].item) == 0) \
402*1a7f22d9SAlan Somers 			continue; \
403*1a7f22d9SAlan Somers 		for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { \
404*1a7f22d9SAlan Somers 			int lun = stats[i].item; \
405*1a7f22d9SAlan Somers 			if (lun >= targdata.ntargets) \
406*1a7f22d9SAlan Somers 			    errx(1, "LUN %u out of range", lun); \
407*1a7f22d9SAlan Somers 			printf("iscsi_target_" #field "{" \
408*1a7f22d9SAlan Somers 			    "lun=\"%u\",target=\"%s\",type=\"%s\"} %" PRIu64 \
409*1a7f22d9SAlan Somers 			    "\n", \
410*1a7f22d9SAlan Somers 			    lun, targdata.targets[lun], iotypes[iotype], \
411*1a7f22d9SAlan Somers 			    stats[i].field[iotype]); \
412*1a7f22d9SAlan Somers 		} \
413*1a7f22d9SAlan Somers 	} \
414*1a7f22d9SAlan Somers 
415*1a7f22d9SAlan Somers #define CTLSTAT_PROMETHEUS_TIMELOOP(field) \
416*1a7f22d9SAlan Somers 	for (i = n = 0; i < ctx->cur_items; i++) { \
417*1a7f22d9SAlan Somers 		if (F_MASK(ctx) && bit_test(ctx->item_mask, \
418*1a7f22d9SAlan Somers 		    (int)stats[i].item) == 0) \
419*1a7f22d9SAlan Somers 			continue; \
420*1a7f22d9SAlan Somers 		for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { \
421*1a7f22d9SAlan Somers 			uint64_t us; \
422*1a7f22d9SAlan Somers 			struct timespec ts; \
423*1a7f22d9SAlan Somers 			int lun = stats[i].item; \
424*1a7f22d9SAlan Somers 			if (lun >= targdata.ntargets) \
425*1a7f22d9SAlan Somers 			    errx(1, "LUN %u out of range", lun); \
426*1a7f22d9SAlan Somers 			bintime2timespec(&stats[i].field[iotype], &ts); \
427*1a7f22d9SAlan Somers 			us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000; \
428*1a7f22d9SAlan Somers 			printf("iscsi_target_" #field "{" \
429*1a7f22d9SAlan Somers 			    "lun=\"%u\",target=\"%s\",type=\"%s\"} %" PRIu64 \
430*1a7f22d9SAlan Somers 			    "\n", \
431*1a7f22d9SAlan Somers 			    lun, targdata.targets[lun], iotypes[iotype], us); \
432*1a7f22d9SAlan Somers 		} \
433*1a7f22d9SAlan Somers 	} \
434*1a7f22d9SAlan Somers 
435*1a7f22d9SAlan Somers static void
436*1a7f22d9SAlan Somers cctl_start_pelement(void *user_data, const char *name, const char **attr __unused)
437*1a7f22d9SAlan Somers {
438*1a7f22d9SAlan Somers 	struct cctl_portlist_data* targdata = user_data;
439*1a7f22d9SAlan Somers 
440*1a7f22d9SAlan Somers 	targdata->level++;
441*1a7f22d9SAlan Somers 	if ((u_int)targdata->level >= (sizeof(targdata->cur_sb) /
442*1a7f22d9SAlan Somers 	    sizeof(targdata->cur_sb[0])))
443*1a7f22d9SAlan Somers 		errx(1, "%s: too many nesting levels, %zd max", __func__,
444*1a7f22d9SAlan Somers 		     sizeof(targdata->cur_sb) / sizeof(targdata->cur_sb[0]));
445*1a7f22d9SAlan Somers 
446*1a7f22d9SAlan Somers 	targdata->cur_sb[targdata->level] = sbuf_new_auto();
447*1a7f22d9SAlan Somers 	if (targdata->cur_sb[targdata->level] == NULL)
448*1a7f22d9SAlan Somers 		err(1, "%s: Unable to allocate sbuf", __func__);
449*1a7f22d9SAlan Somers 
450*1a7f22d9SAlan Somers 	if (strcmp(name, "targ_port") == 0) {
451*1a7f22d9SAlan Somers 		targdata->lun = -1;
452*1a7f22d9SAlan Somers 		free(targdata->target);
453*1a7f22d9SAlan Somers 		targdata->target = NULL;
454*1a7f22d9SAlan Somers 	}
455*1a7f22d9SAlan Somers }
456*1a7f22d9SAlan Somers 
457*1a7f22d9SAlan Somers static void
458*1a7f22d9SAlan Somers cctl_char_phandler(void *user_data, const XML_Char *str, int len)
459*1a7f22d9SAlan Somers {
460*1a7f22d9SAlan Somers 	struct cctl_portlist_data *targdata = user_data;
461*1a7f22d9SAlan Somers 
462*1a7f22d9SAlan Somers 	sbuf_bcat(targdata->cur_sb[targdata->level], str, len);
463*1a7f22d9SAlan Somers }
464*1a7f22d9SAlan Somers 
465*1a7f22d9SAlan Somers static void
466*1a7f22d9SAlan Somers cctl_end_pelement(void *user_data, const char *name)
467*1a7f22d9SAlan Somers {
468*1a7f22d9SAlan Somers 	struct cctl_portlist_data* targdata = user_data;
469*1a7f22d9SAlan Somers 	char *str;
470*1a7f22d9SAlan Somers 
471*1a7f22d9SAlan Somers 	if (targdata->cur_sb[targdata->level] == NULL)
472*1a7f22d9SAlan Somers 		errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
473*1a7f22d9SAlan Somers 		     targdata->level, name);
474*1a7f22d9SAlan Somers 
475*1a7f22d9SAlan Somers 	if (sbuf_finish(targdata->cur_sb[targdata->level]) != 0)
476*1a7f22d9SAlan Somers 		err(1, "%s: sbuf_finish", __func__);
477*1a7f22d9SAlan Somers 	str = strdup(sbuf_data(targdata->cur_sb[targdata->level]));
478*1a7f22d9SAlan Somers 	if (str == NULL)
479*1a7f22d9SAlan Somers 		err(1, "%s can't allocate %zd bytes for string", __func__,
480*1a7f22d9SAlan Somers 		    sbuf_len(targdata->cur_sb[targdata->level]));
481*1a7f22d9SAlan Somers 
482*1a7f22d9SAlan Somers 	sbuf_delete(targdata->cur_sb[targdata->level]);
483*1a7f22d9SAlan Somers 	targdata->cur_sb[targdata->level] = NULL;
484*1a7f22d9SAlan Somers 	targdata->level--;
485*1a7f22d9SAlan Somers 
486*1a7f22d9SAlan Somers 	if (strcmp(name, "target") == 0) {
487*1a7f22d9SAlan Somers 		free(targdata->target);
488*1a7f22d9SAlan Somers 		targdata->target = str;
489*1a7f22d9SAlan Somers 	} else if (strcmp(name, "lun") == 0) {
490*1a7f22d9SAlan Somers 		targdata->lun = atoi(str);
491*1a7f22d9SAlan Somers 		free(str);
492*1a7f22d9SAlan Somers 	} else if (strcmp(name, "targ_port") == 0) {
493*1a7f22d9SAlan Somers 		if (targdata->lun >= 0 && targdata->target != NULL) {
494*1a7f22d9SAlan Somers 			if (targdata->lun >= targdata->ntargets) {
495*1a7f22d9SAlan Somers 				/*
496*1a7f22d9SAlan Somers 				 * This can happen for example if there are
497*1a7f22d9SAlan Somers 				 * holes in CTL's lunlist.
498*1a7f22d9SAlan Somers 				 */
499*1a7f22d9SAlan Somers 				targdata->ntargets = MAX(targdata->ntargets * 2,
500*1a7f22d9SAlan Somers 					targdata->lun + 1);
501*1a7f22d9SAlan Somers 				size_t newsize = targdata->ntargets *
502*1a7f22d9SAlan Somers 					sizeof(char*);
503*1a7f22d9SAlan Somers 				targdata->targets = rallocx(targdata->targets,
504*1a7f22d9SAlan Somers 					newsize, MALLOCX_ZERO);
505*1a7f22d9SAlan Somers 			}
506*1a7f22d9SAlan Somers 			free(targdata->targets[targdata->lun]);
507*1a7f22d9SAlan Somers 			targdata->targets[targdata->lun] = targdata->target;
508*1a7f22d9SAlan Somers 			targdata->target = NULL;
509*1a7f22d9SAlan Somers 		}
510*1a7f22d9SAlan Somers 		free(str);
511*1a7f22d9SAlan Somers 	} else {
512*1a7f22d9SAlan Somers 		free(str);
513*1a7f22d9SAlan Somers 	}
514*1a7f22d9SAlan Somers }
515*1a7f22d9SAlan Somers 
516*1a7f22d9SAlan Somers static void
517*1a7f22d9SAlan Somers ctlstat_prometheus(int fd, struct ctlstat_context *ctx) {
518*1a7f22d9SAlan Somers 	struct ctl_io_stats *stats = ctx->cur_stats;
519*1a7f22d9SAlan Somers 	struct ctl_lun_list list;
520*1a7f22d9SAlan Somers 	struct cctl_portlist_data targdata;
521*1a7f22d9SAlan Somers 	XML_Parser parser;
522*1a7f22d9SAlan Somers 	char *port_str = NULL;
523*1a7f22d9SAlan Somers 	int iotype, i, n, retval;
524*1a7f22d9SAlan Somers 	int port_len = 4096;
525*1a7f22d9SAlan Somers 
526*1a7f22d9SAlan Somers 	bzero(&targdata, sizeof(targdata));
527*1a7f22d9SAlan Somers 	targdata.ntargets = ctx->cur_items;
528*1a7f22d9SAlan Somers 	targdata.targets = calloc(targdata.ntargets, sizeof(char*));
529*1a7f22d9SAlan Somers retry:
530*1a7f22d9SAlan Somers 	port_str = (char *)realloc(port_str, port_len);
531*1a7f22d9SAlan Somers 	bzero(&list, sizeof(list));
532*1a7f22d9SAlan Somers 	list.alloc_len = port_len;
533*1a7f22d9SAlan Somers 	list.status = CTL_LUN_LIST_NONE;
534*1a7f22d9SAlan Somers 	list.lun_xml = port_str;
535*1a7f22d9SAlan Somers 	if (ioctl(fd, CTL_PORT_LIST, &list) == -1)
536*1a7f22d9SAlan Somers 		err(1, "%s: error issuing CTL_PORT_LIST ioctl", __func__);
537*1a7f22d9SAlan Somers 	if (list.status == CTL_LUN_LIST_ERROR) {
538*1a7f22d9SAlan Somers 		warnx("%s: error returned from CTL_PORT_LIST ioctl:\n%s",
539*1a7f22d9SAlan Somers 		      __func__, list.error_str);
540*1a7f22d9SAlan Somers 	} else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
541*1a7f22d9SAlan Somers 		port_len <<= 1;
542*1a7f22d9SAlan Somers 		goto retry;
543*1a7f22d9SAlan Somers 	}
544*1a7f22d9SAlan Somers 
545*1a7f22d9SAlan Somers 	parser = XML_ParserCreate(NULL);
546*1a7f22d9SAlan Somers 	if (parser == NULL)
547*1a7f22d9SAlan Somers 		err(1, "%s: Unable to create XML parser", __func__);
548*1a7f22d9SAlan Somers 	XML_SetUserData(parser, &targdata);
549*1a7f22d9SAlan Somers 	XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement);
550*1a7f22d9SAlan Somers 	XML_SetCharacterDataHandler(parser, cctl_char_phandler);
551*1a7f22d9SAlan Somers 
552*1a7f22d9SAlan Somers 	retval = XML_Parse(parser, port_str, strlen(port_str), 1);
553*1a7f22d9SAlan Somers 	if (retval != 1) {
554*1a7f22d9SAlan Somers 		errx(1, "%s: Unable to parse XML: Error %d", __func__,
555*1a7f22d9SAlan Somers 		    XML_GetErrorCode(parser));
556*1a7f22d9SAlan Somers 	}
557*1a7f22d9SAlan Somers 	XML_ParserFree(parser);
558*1a7f22d9SAlan Somers 
559*1a7f22d9SAlan Somers 	/*
560*1a7f22d9SAlan Somers 	 * NB: Some clients will print a warning if we don't set Content-Length,
561*1a7f22d9SAlan Somers 	 * but they still work.  And the data still gets into Prometheus.
562*1a7f22d9SAlan Somers 	 */
563*1a7f22d9SAlan Somers 	printf("HTTP/1.1 200 OK\r\n"
564*1a7f22d9SAlan Somers 	       "Connection: close\r\n"
565*1a7f22d9SAlan Somers 	       "Content-Type: text/plain; version=0.0.4\r\n"
566*1a7f22d9SAlan Somers 	       "\r\n");
567*1a7f22d9SAlan Somers 
568*1a7f22d9SAlan Somers 	printf("# HELP iscsi_target_bytes Number of bytes\n"
569*1a7f22d9SAlan Somers 	       "# TYPE iscsi_target_bytes counter\n");
570*1a7f22d9SAlan Somers 	CTLSTAT_PROMETHEUS_LOOP(bytes);
571*1a7f22d9SAlan Somers 	printf("# HELP iscsi_target_dmas Number of DMA\n"
572*1a7f22d9SAlan Somers 	       "# TYPE iscsi_target_dmas counter\n");
573*1a7f22d9SAlan Somers 	CTLSTAT_PROMETHEUS_LOOP(dmas);
574*1a7f22d9SAlan Somers 	printf("# HELP iscsi_target_operations Number of operations\n"
575*1a7f22d9SAlan Somers 	       "# TYPE iscsi_target_operations counter\n");
576*1a7f22d9SAlan Somers 	CTLSTAT_PROMETHEUS_LOOP(operations);
577*1a7f22d9SAlan Somers 	printf("# HELP iscsi_target_time Cumulative operation time in us\n"
578*1a7f22d9SAlan Somers 	       "# TYPE iscsi_target_time counter\n");
579*1a7f22d9SAlan Somers 	CTLSTAT_PROMETHEUS_TIMELOOP(time);
580*1a7f22d9SAlan Somers 	printf("# HELP iscsi_target_dma_time Cumulative DMA time in us\n"
581*1a7f22d9SAlan Somers 	       "# TYPE iscsi_target_dma_time counter\n");
582*1a7f22d9SAlan Somers 	CTLSTAT_PROMETHEUS_TIMELOOP(dma_time);
583*1a7f22d9SAlan Somers 
584*1a7f22d9SAlan Somers 	for (i = 0; i < targdata.ntargets; i++)
585*1a7f22d9SAlan Somers 		free(targdata.targets[i]);
586*1a7f22d9SAlan Somers 	free(targdata.target);
587*1a7f22d9SAlan Somers 	free(targdata.targets);
588*1a7f22d9SAlan Somers 
589*1a7f22d9SAlan Somers 	fflush(stdout);
590*1a7f22d9SAlan Somers }
591*1a7f22d9SAlan Somers 
592130f4520SKenneth D. Merry static void
593130f4520SKenneth D. Merry ctlstat_standard(struct ctlstat_context *ctx) {
5945e796316SKenneth D. Merry 	long double etime;
595130f4520SKenneth D. Merry 	uint64_t delta_jiffies, delta_idle;
596130f4520SKenneth D. Merry 	long double cpu_percentage;
5975be4479bSAlexander Motin 	int i, j, n;
598130f4520SKenneth D. Merry 
599130f4520SKenneth D. Merry 	cpu_percentage = 0;
600130f4520SKenneth D. Merry 
601130f4520SKenneth D. Merry 	if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0))
602130f4520SKenneth D. Merry 		errx(1, "error returned from getcpu()");
603130f4520SKenneth D. Merry 
6045e796316SKenneth D. Merry 	etime = ctx->cur_time.tv_sec - ctx->prev_time.tv_sec +
6055e796316SKenneth D. Merry 	    (ctx->prev_time.tv_nsec - ctx->cur_time.tv_nsec) * 1e-9;
606130f4520SKenneth D. Merry 
607130f4520SKenneth D. Merry 	if (F_CPU(ctx)) {
608130f4520SKenneth D. Merry 		ctx->prev_total_jiffies = ctx->cur_total_jiffies;
609130f4520SKenneth D. Merry 		ctx->cur_total_jiffies = ctx->cur_cpu.user +
610130f4520SKenneth D. Merry 		    ctx->cur_cpu.nice + ctx->cur_cpu.system +
611130f4520SKenneth D. Merry 		    ctx->cur_cpu.intr + ctx->cur_cpu.idle;
612130f4520SKenneth D. Merry 		delta_jiffies = ctx->cur_total_jiffies;
613130f4520SKenneth D. Merry 		if (F_FIRST(ctx) == 0)
614130f4520SKenneth D. Merry 			delta_jiffies -= ctx->prev_total_jiffies;
615130f4520SKenneth D. Merry 		ctx->prev_idle = ctx->cur_idle;
616130f4520SKenneth D. Merry 		ctx->cur_idle = ctx->cur_cpu.idle;
617130f4520SKenneth D. Merry 		delta_idle = ctx->cur_idle - ctx->prev_idle;
618130f4520SKenneth D. Merry 
619130f4520SKenneth D. Merry 		cpu_percentage = delta_jiffies - delta_idle;
620130f4520SKenneth D. Merry 		cpu_percentage /= delta_jiffies;
621130f4520SKenneth D. Merry 		cpu_percentage *= 100;
622130f4520SKenneth D. Merry 	}
623130f4520SKenneth D. Merry 
624130f4520SKenneth D. Merry 	if (F_HDR(ctx)) {
625130f4520SKenneth D. Merry 		ctx->header_interval--;
626130f4520SKenneth D. Merry 		if (ctx->header_interval <= 0) {
627130f4520SKenneth D. Merry 			if (F_CPU(ctx))
628130f4520SKenneth D. Merry 				fprintf(stdout, " CPU");
62961639a0aSAlexander Motin 			if (F_TOTALS(ctx)) {
63061639a0aSAlexander Motin 				fprintf(stdout, "%s     Read       %s"
63161639a0aSAlexander Motin 					"    Write       %s    Total\n",
632bb8f9017SAlexander Motin 					(F_TIMEVAL(ctx) != 0) ? "      " : "",
633bb8f9017SAlexander Motin 					(F_TIMEVAL(ctx) != 0) ? "      " : "",
634bb8f9017SAlexander Motin 					(F_TIMEVAL(ctx) != 0) ? "      " : "");
6355be4479bSAlexander Motin 				n = 3;
63661639a0aSAlexander Motin 			} else {
6375e4b2529SBaptiste Daroussin 				for (i = n = 0; i < min(ctl_stat_bits,
638bb8f9017SAlexander Motin 				     ctx->cur_items); i++) {
639bb8f9017SAlexander Motin 					int item;
640130f4520SKenneth D. Merry 
641130f4520SKenneth D. Merry 					/*
642130f4520SKenneth D. Merry 					 * Obviously this won't work with
643130f4520SKenneth D. Merry 					 * LUN numbers greater than a signed
644130f4520SKenneth D. Merry 					 * integer.
645130f4520SKenneth D. Merry 					 */
646bb8f9017SAlexander Motin 					item = (int)ctx->cur_stats[i].item;
647130f4520SKenneth D. Merry 
648bb8f9017SAlexander Motin 					if (F_MASK(ctx) &&
649bb8f9017SAlexander Motin 					    bit_test(ctx->item_mask, item) == 0)
650130f4520SKenneth D. Merry 						continue;
651d5a6319aSAlexander Motin 					fprintf(stdout, "%15.6s%d %s",
652bb8f9017SAlexander Motin 					    F_PORTS(ctx) ? "port" : "lun", item,
653bb8f9017SAlexander Motin 					    (F_TIMEVAL(ctx) != 0) ? "     " : "");
6545be4479bSAlexander Motin 					if (++n >= ctx->numdevs)
6555be4479bSAlexander Motin 						break;
656130f4520SKenneth D. Merry 				}
657130f4520SKenneth D. Merry 				fprintf(stdout, "\n");
658130f4520SKenneth D. Merry 			}
65961639a0aSAlexander Motin 			if (F_CPU(ctx))
66061639a0aSAlexander Motin 				fprintf(stdout, "    ");
6615be4479bSAlexander Motin 			for (i = 0; i < n; i++)
66261639a0aSAlexander Motin 				fprintf(stdout, "%s KB/t   %s MB/s",
663bb8f9017SAlexander Motin 					(F_TIMEVAL(ctx) != 0) ? "    ms" : "",
664130f4520SKenneth D. Merry 					(F_DMA(ctx) == 0) ? "tps" : "dps");
665130f4520SKenneth D. Merry 			fprintf(stdout, "\n");
666130f4520SKenneth D. Merry 			ctx->header_interval = 20;
667130f4520SKenneth D. Merry 		}
668130f4520SKenneth D. Merry 	}
669130f4520SKenneth D. Merry 
67061639a0aSAlexander Motin 	if (F_CPU(ctx))
67161639a0aSAlexander Motin 		fprintf(stdout, "%3.0Lf%%", cpu_percentage);
672130f4520SKenneth D. Merry 	if (F_TOTALS(ctx) != 0) {
673130f4520SKenneth D. Merry 		long double mbsec[3];
674130f4520SKenneth D. Merry 		long double kb_per_transfer[3];
675130f4520SKenneth D. Merry 		long double transfers_per_sec[3];
676130f4520SKenneth D. Merry 		long double ms_per_transfer[3];
677130f4520SKenneth D. Merry 		long double ms_per_dma[3];
678130f4520SKenneth D. Merry 		long double dmas_per_sec[3];
679130f4520SKenneth D. Merry 
680130f4520SKenneth D. Merry 		for (i = 0; i < 3; i++)
681130f4520SKenneth D. Merry 			ctx->prev_total_stats[i] = ctx->cur_total_stats[i];
682130f4520SKenneth D. Merry 
683130f4520SKenneth D. Merry 		memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats));
684130f4520SKenneth D. Merry 
685130f4520SKenneth D. Merry 		/* Use macros to make the next loop more readable. */
686bb8f9017SAlexander Motin #define	ADD_STATS_BYTES(st, i, j) \
687bb8f9017SAlexander Motin 	ctx->cur_total_stats[st].bytes[j] += \
688bb8f9017SAlexander Motin 	    ctx->cur_stats[i].bytes[j]
689bb8f9017SAlexander Motin #define	ADD_STATS_OPERATIONS(st, i, j) \
690bb8f9017SAlexander Motin 	ctx->cur_total_stats[st].operations[j] += \
691bb8f9017SAlexander Motin 	    ctx->cur_stats[i].operations[j]
692bb8f9017SAlexander Motin #define	ADD_STATS_DMAS(st, i, j) \
693bb8f9017SAlexander Motin 	ctx->cur_total_stats[st].dmas[j] += \
694bb8f9017SAlexander Motin 	    ctx->cur_stats[i].dmas[j]
695bb8f9017SAlexander Motin #define	ADD_STATS_TIME(st, i, j) \
696bb8f9017SAlexander Motin 	bintime_add(&ctx->cur_total_stats[st].time[j], \
697bb8f9017SAlexander Motin 	    &ctx->cur_stats[i].time[j])
698bb8f9017SAlexander Motin #define	ADD_STATS_DMA_TIME(st, i, j) \
699bb8f9017SAlexander Motin 	bintime_add(&ctx->cur_total_stats[st].dma_time[j], \
700bb8f9017SAlexander Motin 	    &ctx->cur_stats[i].dma_time[j])
701130f4520SKenneth D. Merry 
702bb8f9017SAlexander Motin 		for (i = 0; i < ctx->cur_items; i++) {
703bb8f9017SAlexander Motin 			if (F_MASK(ctx) && bit_test(ctx->item_mask,
704bb8f9017SAlexander Motin 			    (int)ctx->cur_stats[i].item) == 0)
70561639a0aSAlexander Motin 				continue;
706130f4520SKenneth D. Merry 			for (j = 0; j < CTL_STATS_NUM_TYPES; j++) {
707bb8f9017SAlexander Motin 				ADD_STATS_BYTES(2, i, j);
708bb8f9017SAlexander Motin 				ADD_STATS_OPERATIONS(2, i, j);
709bb8f9017SAlexander Motin 				ADD_STATS_DMAS(2, i, j);
710bb8f9017SAlexander Motin 				ADD_STATS_TIME(2, i, j);
711bb8f9017SAlexander Motin 				ADD_STATS_DMA_TIME(2, i, j);
712130f4520SKenneth D. Merry 			}
713bb8f9017SAlexander Motin 			ADD_STATS_BYTES(0, i, CTL_STATS_READ);
714bb8f9017SAlexander Motin 			ADD_STATS_OPERATIONS(0, i, CTL_STATS_READ);
715bb8f9017SAlexander Motin 			ADD_STATS_DMAS(0, i, CTL_STATS_READ);
716bb8f9017SAlexander Motin 			ADD_STATS_TIME(0, i, CTL_STATS_READ);
717bb8f9017SAlexander Motin 			ADD_STATS_DMA_TIME(0, i, CTL_STATS_READ);
718130f4520SKenneth D. Merry 
719bb8f9017SAlexander Motin 			ADD_STATS_BYTES(1, i, CTL_STATS_WRITE);
720bb8f9017SAlexander Motin 			ADD_STATS_OPERATIONS(1, i, CTL_STATS_WRITE);
721bb8f9017SAlexander Motin 			ADD_STATS_DMAS(1, i, CTL_STATS_WRITE);
722bb8f9017SAlexander Motin 			ADD_STATS_TIME(1, i, CTL_STATS_WRITE);
723bb8f9017SAlexander Motin 			ADD_STATS_DMA_TIME(1, i, CTL_STATS_WRITE);
724130f4520SKenneth D. Merry 		}
725130f4520SKenneth D. Merry 
726130f4520SKenneth D. Merry 		for (i = 0; i < 3; i++) {
727bb8f9017SAlexander Motin 			compute_stats(&ctx->cur_total_stats[i],
728130f4520SKenneth D. Merry 				F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i],
729130f4520SKenneth D. Merry 				etime, &mbsec[i], &kb_per_transfer[i],
730130f4520SKenneth D. Merry 				&transfers_per_sec[i],
731130f4520SKenneth D. Merry 				&ms_per_transfer[i], &ms_per_dma[i],
732130f4520SKenneth D. Merry 				&dmas_per_sec[i]);
733130f4520SKenneth D. Merry 			if (F_DMA(ctx) != 0)
73461639a0aSAlexander Motin 				fprintf(stdout, " %5.1Lf",
735130f4520SKenneth D. Merry 					ms_per_dma[i]);
736bb8f9017SAlexander Motin 			else if (F_TIMEVAL(ctx) != 0)
73761639a0aSAlexander Motin 				fprintf(stdout, " %5.1Lf",
738130f4520SKenneth D. Merry 					ms_per_transfer[i]);
73961639a0aSAlexander Motin 			fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf",
740130f4520SKenneth D. Merry 				kb_per_transfer[i],
741130f4520SKenneth D. Merry 				(F_DMA(ctx) == 0) ? transfers_per_sec[i] :
742130f4520SKenneth D. Merry 				dmas_per_sec[i], mbsec[i]);
743130f4520SKenneth D. Merry 		}
744130f4520SKenneth D. Merry 	} else {
7455e4b2529SBaptiste Daroussin 		for (i = n = 0; i < min(ctl_stat_bits, ctx->cur_items); i++) {
746130f4520SKenneth D. Merry 			long double mbsec, kb_per_transfer;
747130f4520SKenneth D. Merry 			long double transfers_per_sec;
748130f4520SKenneth D. Merry 			long double ms_per_transfer;
749130f4520SKenneth D. Merry 			long double ms_per_dma;
750130f4520SKenneth D. Merry 			long double dmas_per_sec;
751130f4520SKenneth D. Merry 
752bb8f9017SAlexander Motin 			if (F_MASK(ctx) && bit_test(ctx->item_mask,
753bb8f9017SAlexander Motin 			    (int)ctx->cur_stats[i].item) == 0)
754130f4520SKenneth D. Merry 				continue;
755bb8f9017SAlexander Motin 			for (j = 0; j < ctx->prev_items; j++) {
756bb8f9017SAlexander Motin 				if (ctx->prev_stats[j].item ==
757bb8f9017SAlexander Motin 				    ctx->cur_stats[i].item)
758bb8f9017SAlexander Motin 					break;
759bb8f9017SAlexander Motin 			}
760bb8f9017SAlexander Motin 			if (j >= ctx->prev_items)
761bb8f9017SAlexander Motin 				j = -1;
762bb8f9017SAlexander Motin 			compute_stats(&ctx->cur_stats[i],
763bb8f9017SAlexander Motin 			    j >= 0 ? &ctx->prev_stats[j] : NULL,
76461639a0aSAlexander Motin 			    etime, &mbsec, &kb_per_transfer,
765130f4520SKenneth D. Merry 			    &transfers_per_sec, &ms_per_transfer,
766130f4520SKenneth D. Merry 			    &ms_per_dma, &dmas_per_sec);
767130f4520SKenneth D. Merry 			if (F_DMA(ctx))
76861639a0aSAlexander Motin 				fprintf(stdout, " %5.1Lf",
769130f4520SKenneth D. Merry 					ms_per_dma);
770bb8f9017SAlexander Motin 			else if (F_TIMEVAL(ctx) != 0)
77161639a0aSAlexander Motin 				fprintf(stdout, " %5.1Lf",
772130f4520SKenneth D. Merry 					ms_per_transfer);
77361639a0aSAlexander Motin 			fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf",
774130f4520SKenneth D. Merry 				kb_per_transfer, (F_DMA(ctx) == 0) ?
775130f4520SKenneth D. Merry 				transfers_per_sec : dmas_per_sec, mbsec);
7765be4479bSAlexander Motin 			if (++n >= ctx->numdevs)
7775be4479bSAlexander Motin 				break;
778130f4520SKenneth D. Merry 		}
779130f4520SKenneth D. Merry 	}
780130f4520SKenneth D. Merry }
781130f4520SKenneth D. Merry 
782130f4520SKenneth D. Merry int
783130f4520SKenneth D. Merry main(int argc, char **argv)
784130f4520SKenneth D. Merry {
785130f4520SKenneth D. Merry 	int c;
786130f4520SKenneth D. Merry 	int count, waittime;
787130f4520SKenneth D. Merry 	int fd, retval;
7885e4b2529SBaptiste Daroussin 	size_t size;
789130f4520SKenneth D. Merry 	struct ctlstat_context ctx;
790bb8f9017SAlexander Motin 	struct ctl_io_stats *tmp_stats;
791130f4520SKenneth D. Merry 
792130f4520SKenneth D. Merry 	/* default values */
793130f4520SKenneth D. Merry 	retval = 0;
794130f4520SKenneth D. Merry 	waittime = 1;
795130f4520SKenneth D. Merry 	count = -1;
796130f4520SKenneth D. Merry 	memset(&ctx, 0, sizeof(ctx));
797130f4520SKenneth D. Merry 	ctx.numdevs = 3;
798130f4520SKenneth D. Merry 	ctx.mode = CTLSTAT_MODE_STANDARD;
799130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_CPU;
800130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_FIRST_RUN;
801130f4520SKenneth D. Merry 	ctx.flags |= CTLSTAT_FLAG_HEADER;
802130f4520SKenneth D. Merry 
8035e4b2529SBaptiste Daroussin 	size = sizeof(ctl_stat_bits);
8045e4b2529SBaptiste Daroussin 	if (sysctlbyname("kern.cam.ctl.max_luns", &ctl_stat_bits, &size, NULL,
8055e4b2529SBaptiste Daroussin 	    0) == -1) {
8065e4b2529SBaptiste Daroussin 		/* Backward compatibility for where the sysctl wasn't exposed */
8075e4b2529SBaptiste Daroussin 		ctl_stat_bits = 1024;
8085e4b2529SBaptiste Daroussin 	}
8095e4b2529SBaptiste Daroussin 	ctx.item_mask = bit_alloc(ctl_stat_bits);
8105e4b2529SBaptiste Daroussin 	if (ctx.item_mask == NULL)
8115e4b2529SBaptiste Daroussin 		err(1, "bit_alloc() failed");
8125e4b2529SBaptiste Daroussin 
813130f4520SKenneth D. Merry 	while ((c = getopt(argc, argv, ctlstat_opts)) != -1) {
814130f4520SKenneth D. Merry 		switch (c) {
815130f4520SKenneth D. Merry 		case 'C':
816130f4520SKenneth D. Merry 			ctx.flags &= ~CTLSTAT_FLAG_CPU;
817130f4520SKenneth D. Merry 			break;
818130f4520SKenneth D. Merry 		case 'c':
819130f4520SKenneth D. Merry 			count = atoi(optarg);
820130f4520SKenneth D. Merry 			break;
821130f4520SKenneth D. Merry 		case 'd':
822130f4520SKenneth D. Merry 			ctx.flags |= CTLSTAT_FLAG_DMA_TIME;
823130f4520SKenneth D. Merry 			break;
824130f4520SKenneth D. Merry 		case 'D':
825130f4520SKenneth D. Merry 			ctx.mode = CTLSTAT_MODE_DUMP;
826130f4520SKenneth D. Merry 			waittime = 30;
827130f4520SKenneth D. Merry 			break;
828130f4520SKenneth D. Merry 		case 'h':
829130f4520SKenneth D. Merry 			ctx.flags &= ~CTLSTAT_FLAG_HEADER;
830130f4520SKenneth D. Merry 			break;
831130f4520SKenneth D. Merry 		case 'j':
832130f4520SKenneth D. Merry 			ctx.mode = CTLSTAT_MODE_JSON;
833130f4520SKenneth D. Merry 			waittime = 30;
834130f4520SKenneth D. Merry 			break;
835130f4520SKenneth D. Merry 		case 'l': {
836130f4520SKenneth D. Merry 			int cur_lun;
837130f4520SKenneth D. Merry 
838130f4520SKenneth D. Merry 			cur_lun = atoi(optarg);
8395e4b2529SBaptiste Daroussin 			if (cur_lun > ctl_stat_bits)
840130f4520SKenneth D. Merry 				errx(1, "Invalid LUN number %d", cur_lun);
841130f4520SKenneth D. Merry 
842bb8f9017SAlexander Motin 			if (!F_MASK(&ctx))
843130f4520SKenneth D. Merry 				ctx.numdevs = 1;
844130f4520SKenneth D. Merry 			else
845130f4520SKenneth D. Merry 				ctx.numdevs++;
846bb8f9017SAlexander Motin 			bit_set(ctx.item_mask, cur_lun);
847bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_MASK;
848bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_LUNS;
849130f4520SKenneth D. Merry 			break;
850130f4520SKenneth D. Merry 		}
851130f4520SKenneth D. Merry 		case 'n':
852130f4520SKenneth D. Merry 			ctx.numdevs = atoi(optarg);
853130f4520SKenneth D. Merry 			break;
85461639a0aSAlexander Motin 		case 'p': {
85561639a0aSAlexander Motin 			int cur_port;
85661639a0aSAlexander Motin 
85761639a0aSAlexander Motin 			cur_port = atoi(optarg);
8585e4b2529SBaptiste Daroussin 			if (cur_port > ctl_stat_bits)
859bb8f9017SAlexander Motin 				errx(1, "Invalid port number %d", cur_port);
86061639a0aSAlexander Motin 
861bb8f9017SAlexander Motin 			if (!F_MASK(&ctx))
862bb8f9017SAlexander Motin 				ctx.numdevs = 1;
863bb8f9017SAlexander Motin 			else
864bb8f9017SAlexander Motin 				ctx.numdevs++;
865bb8f9017SAlexander Motin 			bit_set(ctx.item_mask, cur_port);
866bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_MASK;
867bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_PORTS;
86861639a0aSAlexander Motin 			break;
86961639a0aSAlexander Motin 		}
870*1a7f22d9SAlan Somers 		case 'P':
871*1a7f22d9SAlan Somers 			ctx.mode = CTLSTAT_MODE_PROMETHEUS;
872*1a7f22d9SAlan Somers 			break;
873130f4520SKenneth D. Merry 		case 't':
874130f4520SKenneth D. Merry 			ctx.flags |= CTLSTAT_FLAG_TOTALS;
875130f4520SKenneth D. Merry 			break;
876130f4520SKenneth D. Merry 		case 'w':
877130f4520SKenneth D. Merry 			waittime = atoi(optarg);
878130f4520SKenneth D. Merry 			break;
879130f4520SKenneth D. Merry 		default:
880130f4520SKenneth D. Merry 			retval = 1;
881130f4520SKenneth D. Merry 			usage(retval);
882130f4520SKenneth D. Merry 			exit(retval);
883130f4520SKenneth D. Merry 			break;
884130f4520SKenneth D. Merry 		}
885130f4520SKenneth D. Merry 	}
886130f4520SKenneth D. Merry 
887bb8f9017SAlexander Motin 	if (F_LUNS(&ctx) && F_PORTS(&ctx))
888bb8f9017SAlexander Motin 		errx(1, "Options -p and -l are exclusive.");
889bb8f9017SAlexander Motin 
890*1a7f22d9SAlan Somers 	if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) {
891*1a7f22d9SAlan Somers 		if ((count != -1) ||
892*1a7f22d9SAlan Somers 			(waittime != 1) ||
893*1a7f22d9SAlan Somers 			/* NB: -P could be compatible with -t in the future */
894*1a7f22d9SAlan Somers 			(ctx.flags & CTLSTAT_FLAG_TOTALS))
895*1a7f22d9SAlan Somers 		{
896*1a7f22d9SAlan Somers 			errx(1, "Option -P is exclusive with -c, -w, and -t");
897*1a7f22d9SAlan Somers 		}
898*1a7f22d9SAlan Somers 		count = 1;
899*1a7f22d9SAlan Somers 	}
900*1a7f22d9SAlan Somers 
901bb8f9017SAlexander Motin 	if (!F_LUNS(&ctx) && !F_PORTS(&ctx)) {
902bb8f9017SAlexander Motin 		if (F_TOTALS(&ctx))
903bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_PORTS;
904bb8f9017SAlexander Motin 		else
905bb8f9017SAlexander Motin 			ctx.flags |= CTLSTAT_FLAG_LUNS;
906bb8f9017SAlexander Motin 	}
907bb8f9017SAlexander Motin 
908130f4520SKenneth D. Merry 	if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1)
909130f4520SKenneth D. Merry 		err(1, "cannot open %s", CTL_DEFAULT_DEV);
910130f4520SKenneth D. Merry 
911130f4520SKenneth D. Merry 	for (;count != 0;) {
912bb8f9017SAlexander Motin 		tmp_stats = ctx.prev_stats;
913bb8f9017SAlexander Motin 		ctx.prev_stats = ctx.cur_stats;
914bb8f9017SAlexander Motin 		ctx.cur_stats = tmp_stats;
915bb8f9017SAlexander Motin 		c = ctx.prev_alloc;
916bb8f9017SAlexander Motin 		ctx.prev_alloc = ctx.cur_alloc;
917bb8f9017SAlexander Motin 		ctx.cur_alloc = c;
918bb8f9017SAlexander Motin 		c = ctx.prev_items;
919bb8f9017SAlexander Motin 		ctx.prev_items = ctx.cur_items;
920bb8f9017SAlexander Motin 		ctx.cur_items = c;
921130f4520SKenneth D. Merry 		ctx.prev_time = ctx.cur_time;
922130f4520SKenneth D. Merry 		ctx.prev_cpu = ctx.cur_cpu;
923bb8f9017SAlexander Motin 		if (getstats(fd, &ctx.cur_alloc, &ctx.cur_items,
924bb8f9017SAlexander Motin 		    &ctx.cur_stats, &ctx.cur_time, &ctx.flags) != 0)
925130f4520SKenneth D. Merry 			errx(1, "error returned from getstats()");
926130f4520SKenneth D. Merry 
927130f4520SKenneth D. Merry 		switch(ctx.mode) {
928130f4520SKenneth D. Merry 		case CTLSTAT_MODE_STANDARD:
929130f4520SKenneth D. Merry 			ctlstat_standard(&ctx);
930130f4520SKenneth D. Merry 			break;
931130f4520SKenneth D. Merry 		case CTLSTAT_MODE_DUMP:
932130f4520SKenneth D. Merry 			ctlstat_dump(&ctx);
933130f4520SKenneth D. Merry 			break;
934130f4520SKenneth D. Merry 		case CTLSTAT_MODE_JSON:
935130f4520SKenneth D. Merry 			ctlstat_json(&ctx);
936130f4520SKenneth D. Merry 			break;
937*1a7f22d9SAlan Somers 		case CTLSTAT_MODE_PROMETHEUS:
938*1a7f22d9SAlan Somers 			ctlstat_prometheus(fd, &ctx);
939*1a7f22d9SAlan Somers 			break;
940130f4520SKenneth D. Merry 		default:
941130f4520SKenneth D. Merry 			break;
942130f4520SKenneth D. Merry 		}
943130f4520SKenneth D. Merry 
944130f4520SKenneth D. Merry 		fprintf(stdout, "\n");
945fcc87341SAlexander Motin 		fflush(stdout);
946130f4520SKenneth D. Merry 		ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN;
947130f4520SKenneth D. Merry 		if (count != 1)
948130f4520SKenneth D. Merry 			sleep(waittime);
949130f4520SKenneth D. Merry 		if (count > 0)
950130f4520SKenneth D. Merry 			count--;
951130f4520SKenneth D. Merry 	}
952130f4520SKenneth D. Merry 
953130f4520SKenneth D. Merry 	exit (retval);
954130f4520SKenneth D. Merry }
955130f4520SKenneth D. Merry 
956130f4520SKenneth D. Merry /*
957130f4520SKenneth D. Merry  * vim: ts=8
958130f4520SKenneth D. Merry  */
959