xref: /freebsd/usr.bin/systat/iolat.c (revision 22054f88914b51113f77f6eccc11353a891f9f3e)
1*22054f88SWarner Losh /*
2*22054f88SWarner Losh  * Copyright (c) 2021 Netflix, Inc
3*22054f88SWarner Losh  *
4*22054f88SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
5*22054f88SWarner Losh  */
6*22054f88SWarner Losh 
7*22054f88SWarner Losh 
8*22054f88SWarner Losh #include <sys/param.h>
9*22054f88SWarner Losh #include <sys/sysctl.h>
10*22054f88SWarner Losh #include <sys/resource.h>
11*22054f88SWarner Losh 
12*22054f88SWarner Losh #include <devstat.h>
13*22054f88SWarner Losh #include <err.h>
14*22054f88SWarner Losh #include <errno.h>
15*22054f88SWarner Losh #include <math.h>
16*22054f88SWarner Losh #include <stdbool.h>
17*22054f88SWarner Losh #include <stdlib.h>
18*22054f88SWarner Losh #include <string.h>
19*22054f88SWarner Losh 
20*22054f88SWarner Losh #include <sys/queue.h>
21*22054f88SWarner Losh #include <sys/sysctl.h>
22*22054f88SWarner Losh 
23*22054f88SWarner Losh #include "systat.h"
24*22054f88SWarner Losh #include "extern.h"
25*22054f88SWarner Losh #include "devs.h"
26*22054f88SWarner Losh 
27*22054f88SWarner Losh #define CAM_BASE "kern.cam"
28*22054f88SWarner Losh #define LATENCY ".latencies"
29*22054f88SWarner Losh #define CAM_IOSCHED_BASE "kern.cam.iosched.bucket_base_us"
30*22054f88SWarner Losh 
31*22054f88SWarner Losh #define DEV_NAMSIZE	32
32*22054f88SWarner Losh #define OP_NAMSIZE	16
33*22054f88SWarner Losh #define MAX_LATS	32
34*22054f88SWarner Losh 
35*22054f88SWarner Losh static double high_thresh = 500;
36*22054f88SWarner Losh static double med_thresh = 300;
37*22054f88SWarner Losh static bool docolor = true;
38*22054f88SWarner Losh 
39*22054f88SWarner Losh static int ndevs;
40*22054f88SWarner Losh static SLIST_HEAD(, iosched_stat)	curlist;
41*22054f88SWarner Losh 
42*22054f88SWarner Losh struct iosched_op_stat {
43*22054f88SWarner Losh 	int		nlats;
44*22054f88SWarner Losh 	uint64_t	lats[MAX_LATS];
45*22054f88SWarner Losh 	uint64_t	prev_lats[MAX_LATS];
46*22054f88SWarner Losh };
47*22054f88SWarner Losh 
48*22054f88SWarner Losh enum { OP_READ = 0, OP_WRITE, OP_TRIM, NUM_OPS };
49*22054f88SWarner Losh static const char *ops[NUM_OPS] = { "read", "write", "trim" };
50*22054f88SWarner Losh #define OP_READ_MASK (1 << OP_READ)
51*22054f88SWarner Losh #define OP_WRITE_MASK (1 << OP_WRITE)
52*22054f88SWarner Losh #define OP_TRIM_MASK (1 << OP_TRIM)
53*22054f88SWarner Losh 
54*22054f88SWarner Losh static uint32_t flags = OP_READ_MASK | OP_WRITE_MASK | OP_TRIM_MASK;
55*22054f88SWarner Losh 
56*22054f88SWarner Losh struct iosched_stat {
57*22054f88SWarner Losh 	SLIST_ENTRY(iosched_stat)	 link;
58*22054f88SWarner Losh 	char		dev_name[DEV_NAMSIZE];
59*22054f88SWarner Losh 	int		unit;
60*22054f88SWarner Losh 	struct iosched_op_stat op_stats[NUM_OPS];
61*22054f88SWarner Losh };
62*22054f88SWarner Losh 
63*22054f88SWarner Losh static int	name2oid(const char *, int *);
64*22054f88SWarner Losh static int	walk_sysctl(int *, size_t);
65*22054f88SWarner Losh 
66*22054f88SWarner Losh static int
name2oid(const char * name,int * oidp)67*22054f88SWarner Losh name2oid(const char *name, int *oidp)
68*22054f88SWarner Losh {
69*22054f88SWarner Losh 	int oid[2];
70*22054f88SWarner Losh 	int i;
71*22054f88SWarner Losh 	size_t j;
72*22054f88SWarner Losh 
73*22054f88SWarner Losh 	oid[0] = CTL_SYSCTL;
74*22054f88SWarner Losh 	oid[1] = CTL_SYSCTL_NAME2OID;
75*22054f88SWarner Losh 
76*22054f88SWarner Losh 	j = CTL_MAXNAME * sizeof(int);
77*22054f88SWarner Losh 	i = sysctl(oid, 2, oidp, &j, name, strlen(name));
78*22054f88SWarner Losh 	if (i < 0)
79*22054f88SWarner Losh 		return (i);
80*22054f88SWarner Losh 	j /= sizeof(int);
81*22054f88SWarner Losh 	return (j);
82*22054f88SWarner Losh }
83*22054f88SWarner Losh 
84*22054f88SWarner Losh static size_t /* Includes the trailing NUL */
oid2name(int * oid,size_t nlen,char * name,size_t namlen)85*22054f88SWarner Losh oid2name(int *oid, size_t nlen, char *name, size_t namlen)
86*22054f88SWarner Losh {
87*22054f88SWarner Losh 	int qoid[CTL_MAXNAME + 2];
88*22054f88SWarner Losh 	int i;
89*22054f88SWarner Losh 	size_t j;
90*22054f88SWarner Losh 
91*22054f88SWarner Losh 	bzero(name, namlen);
92*22054f88SWarner Losh 	qoid[0] = CTL_SYSCTL;
93*22054f88SWarner Losh 	qoid[1] = CTL_SYSCTL_NAME;
94*22054f88SWarner Losh 	memcpy(qoid + 2, oid, nlen * sizeof(int));
95*22054f88SWarner Losh 	j = namlen;
96*22054f88SWarner Losh 	i = sysctl(qoid, nlen + 2, name, &j, 0, 0);
97*22054f88SWarner Losh 	if (i || !j)
98*22054f88SWarner Losh 		err(1, "sysctl name %d %zu %d", i, j, errno);
99*22054f88SWarner Losh 	return (j);
100*22054f88SWarner Losh }
101*22054f88SWarner Losh 
102*22054f88SWarner Losh static int
oidfmt(int * oid,int len,u_int * kind)103*22054f88SWarner Losh oidfmt(int *oid, int len, u_int *kind)
104*22054f88SWarner Losh {
105*22054f88SWarner Losh 	int qoid[CTL_MAXNAME+2];
106*22054f88SWarner Losh 	u_char buf[BUFSIZ];
107*22054f88SWarner Losh 	int i;
108*22054f88SWarner Losh 	size_t j;
109*22054f88SWarner Losh 
110*22054f88SWarner Losh 	qoid[0] = CTL_SYSCTL;
111*22054f88SWarner Losh 	qoid[1] = CTL_SYSCTL_OIDFMT;
112*22054f88SWarner Losh 	memcpy(qoid + 2, oid, len * sizeof(int));
113*22054f88SWarner Losh 
114*22054f88SWarner Losh 	j = sizeof(buf);
115*22054f88SWarner Losh 	i = sysctl(qoid, len + 2, buf, &j, 0, 0);
116*22054f88SWarner Losh 	if (i)
117*22054f88SWarner Losh 		err(1, "sysctl fmt %d %zu %d", i, j, errno);
118*22054f88SWarner Losh 	*kind = *(u_int *)buf;
119*22054f88SWarner Losh 	return (0);
120*22054f88SWarner Losh }
121*22054f88SWarner Losh 
122*22054f88SWarner Losh static int
split_u64(char * str,const char * delim,uint64_t * buckets,int * nbuckets)123*22054f88SWarner Losh split_u64(char *str, const char *delim, uint64_t *buckets, int *nbuckets)
124*22054f88SWarner Losh {
125*22054f88SWarner Losh 	int n = *nbuckets, i;
126*22054f88SWarner Losh 	char *v;
127*22054f88SWarner Losh 
128*22054f88SWarner Losh 	memset(buckets, 0, n * sizeof(buckets[0]));
129*22054f88SWarner Losh 	for (i = 0; (v = strsep(&str, delim)) != NULL && i < n; i++) {
130*22054f88SWarner Losh 		buckets[i] = strtoull(v, NULL, 10);
131*22054f88SWarner Losh 	}
132*22054f88SWarner Losh 	if (i < n)
133*22054f88SWarner Losh 		*nbuckets = i;
134*22054f88SWarner Losh 	return (i < n);
135*22054f88SWarner Losh }
136*22054f88SWarner Losh 
137*22054f88SWarner Losh static double baselat = 0.000020;
138*22054f88SWarner Losh 
139*22054f88SWarner Losh static float
pest(int permill,uint64_t * lats,int nlat)140*22054f88SWarner Losh pest(int permill, uint64_t *lats, int nlat)
141*22054f88SWarner Losh {
142*22054f88SWarner Losh 	uint64_t tot, samp;
143*22054f88SWarner Losh 	int i;
144*22054f88SWarner Losh 	float b1, b2;
145*22054f88SWarner Losh 
146*22054f88SWarner Losh 	for (tot = 0, i = 0; i < nlat; i++)
147*22054f88SWarner Losh 		tot += lats[i];
148*22054f88SWarner Losh 	if (tot == 0)
149*22054f88SWarner Losh 		return -nanf("");
150*22054f88SWarner Losh 	if (tot < (uint64_t)2000 / (1000 - permill))
151*22054f88SWarner Losh 		return nanf("");
152*22054f88SWarner Losh 	samp = tot * permill / 1000;
153*22054f88SWarner Losh 	if (samp < lats[0])
154*22054f88SWarner Losh 		return baselat * (float)samp / lats[0]; /* linear interpolation 0 and baselat */
155*22054f88SWarner Losh 	for (tot = 0, i = 0; samp >= tot && i < nlat; i++)
156*22054f88SWarner Losh 		tot += lats[i];
157*22054f88SWarner Losh 	i--;
158*22054f88SWarner Losh 	b1 = baselat * (1 << (i - 1));
159*22054f88SWarner Losh 	b2 = baselat * (1 << i);
160*22054f88SWarner Losh 	/* Should expoentially interpolate between buckets -- doing linear instead */
161*22054f88SWarner Losh 	return b1 + (b2 - b1) * (float)(lats[i] - (tot - samp)) / lats[i];
162*22054f88SWarner Losh }
163*22054f88SWarner Losh 
164*22054f88SWarner Losh static int
op2num(const char * op)165*22054f88SWarner Losh op2num(const char *op)
166*22054f88SWarner Losh {
167*22054f88SWarner Losh 	for (int i = 0; i < NUM_OPS; i++)
168*22054f88SWarner Losh 		if (strcmp(op, ops[i]) == 0)
169*22054f88SWarner Losh 			return i;
170*22054f88SWarner Losh 	return -1;
171*22054f88SWarner Losh }
172*22054f88SWarner Losh 
173*22054f88SWarner Losh static struct iosched_op_stat *
find_dev(const char * dev,int unit,int op)174*22054f88SWarner Losh find_dev(const char *dev, int unit, int op)
175*22054f88SWarner Losh {
176*22054f88SWarner Losh 	struct iosched_stat *isp;
177*22054f88SWarner Losh 	struct iosched_op_stat *iosp;
178*22054f88SWarner Losh 
179*22054f88SWarner Losh 	SLIST_FOREACH(isp, &curlist, link) {
180*22054f88SWarner Losh 		if (strcmp(isp->dev_name, dev) != 0 || isp->unit != unit)
181*22054f88SWarner Losh 			continue;
182*22054f88SWarner Losh 		iosp = &isp->op_stats[op];
183*22054f88SWarner Losh 		return iosp;
184*22054f88SWarner Losh 	}
185*22054f88SWarner Losh 	return NULL;
186*22054f88SWarner Losh }
187*22054f88SWarner Losh 
188*22054f88SWarner Losh static struct iosched_op_stat *
alloc_dev(const char * dev,int unit,int op)189*22054f88SWarner Losh alloc_dev(const char *dev, int unit, int op)
190*22054f88SWarner Losh {
191*22054f88SWarner Losh 	struct iosched_stat *isp;
192*22054f88SWarner Losh 	struct iosched_op_stat *iosp;
193*22054f88SWarner Losh 
194*22054f88SWarner Losh 	isp = malloc(sizeof(*isp));
195*22054f88SWarner Losh 	if (isp == NULL)
196*22054f88SWarner Losh 		return NULL;
197*22054f88SWarner Losh 	strlcpy(isp->dev_name, dev, sizeof(isp->dev_name));
198*22054f88SWarner Losh 	isp->unit = unit;
199*22054f88SWarner Losh 	SLIST_INSERT_HEAD(&curlist, isp, link);
200*22054f88SWarner Losh 	ndevs++;
201*22054f88SWarner Losh 	iosp = &isp->op_stats[op];
202*22054f88SWarner Losh 	return iosp;
203*22054f88SWarner Losh }
204*22054f88SWarner Losh 
205*22054f88SWarner Losh #define E3 1000.0
206*22054f88SWarner Losh static void
update_dev(const char * dev,int unit,int op,uint64_t * lats,int nlat)207*22054f88SWarner Losh update_dev(const char *dev, int unit, int op, uint64_t *lats, int nlat)
208*22054f88SWarner Losh {
209*22054f88SWarner Losh 	struct iosched_op_stat *iosp;
210*22054f88SWarner Losh 
211*22054f88SWarner Losh 	iosp = find_dev(dev, unit, op);
212*22054f88SWarner Losh 	if (iosp == NULL)
213*22054f88SWarner Losh 		iosp = alloc_dev(dev, unit, op);
214*22054f88SWarner Losh 	if (iosp == NULL)
215*22054f88SWarner Losh 		return;
216*22054f88SWarner Losh 	iosp->nlats = nlat;
217*22054f88SWarner Losh 	memcpy(iosp->prev_lats, iosp->lats, iosp->nlats * sizeof(uint64_t));
218*22054f88SWarner Losh 	memcpy(iosp->lats, lats, iosp->nlats * sizeof(uint64_t));
219*22054f88SWarner Losh //	printf("%s%d: %-6s %.3f %.3f %.3f %.3f\r\n",
220*22054f88SWarner Losh //	    dev, unit, operation, E3 * pest(500, lats, nlat), E3 * pest(900, lats, nlat),
221*22054f88SWarner Losh //	    E3 * pest(990, lats, nlat), E3 * pest(999, lats, nlat));
222*22054f88SWarner Losh }
223*22054f88SWarner Losh 
224*22054f88SWarner Losh static int
walk_sysctl(int * base_oid,size_t len)225*22054f88SWarner Losh walk_sysctl(int *base_oid, size_t len)
226*22054f88SWarner Losh {
227*22054f88SWarner Losh 	int qoid[CTL_MAXNAME + 2], oid[CTL_MAXNAME];
228*22054f88SWarner Losh 	size_t l1, l2;
229*22054f88SWarner Losh 	char name[BUFSIZ];
230*22054f88SWarner Losh 
231*22054f88SWarner Losh 	if (len > CTL_MAXNAME)
232*22054f88SWarner Losh 		err(1, "Length %zd too long", len);
233*22054f88SWarner Losh 
234*22054f88SWarner Losh 	qoid[0] = CTL_SYSCTL;
235*22054f88SWarner Losh 	qoid[1] = CTL_SYSCTL_NEXT;
236*22054f88SWarner Losh 	l1 = 2;
237*22054f88SWarner Losh 	memcpy(qoid + 2, base_oid, len * sizeof(int));
238*22054f88SWarner Losh 	l1 += len;
239*22054f88SWarner Losh 	for (;;) {
240*22054f88SWarner Losh 		/*
241*22054f88SWarner Losh 		 * Get the next one or return when we get to the end of the
242*22054f88SWarner Losh 		 * sysctls in the kernel.
243*22054f88SWarner Losh 		 */
244*22054f88SWarner Losh 		l2 = sizeof(oid);
245*22054f88SWarner Losh 		if (sysctl(qoid, l1, oid, &l2, 0, 0) != 0) {
246*22054f88SWarner Losh 			if (errno == ENOENT)
247*22054f88SWarner Losh 				return (0);
248*22054f88SWarner Losh 			err(1, "sysctl(getnext) %zu", l2);
249*22054f88SWarner Losh 		}
250*22054f88SWarner Losh 
251*22054f88SWarner Losh 		l2 /= sizeof(int);
252*22054f88SWarner Losh 
253*22054f88SWarner Losh 		/*
254*22054f88SWarner Losh 		 * Bail if we're seeing OIDs that don't have the
255*22054f88SWarner Losh 		 * same prefix or can't have the same prefix.
256*22054f88SWarner Losh 		 */
257*22054f88SWarner Losh 		if (l2 < len ||
258*22054f88SWarner Losh 		    memcmp(oid, base_oid, len * sizeof(int)) != 0)
259*22054f88SWarner Losh 			return (0);
260*22054f88SWarner Losh 
261*22054f88SWarner Losh 		/*
262*22054f88SWarner Losh 		 * Get the name, validate it's one we're looking for,
263*22054f88SWarner Losh 		 * parse the latency and add to list.
264*22054f88SWarner Losh 		 */
265*22054f88SWarner Losh 		do {
266*22054f88SWarner Losh 			int nlat;
267*22054f88SWarner Losh 			size_t l3;
268*22054f88SWarner Losh 			char val[BUFSIZ];
269*22054f88SWarner Losh 			char *walker, *dev, *opstr;
270*22054f88SWarner Losh 			uint64_t latvals[MAX_LATS];
271*22054f88SWarner Losh 			u_int kind;
272*22054f88SWarner Losh 			int unit, op;
273*22054f88SWarner Losh 
274*22054f88SWarner Losh 			l1 = oid2name(oid, l2, name, sizeof(name));
275*22054f88SWarner Losh 			if (strcmp(name + l1 - strlen(LATENCY) - 1, LATENCY) != 0)
276*22054f88SWarner Losh 				break;
277*22054f88SWarner Losh 			if (oidfmt(oid, l2, &kind) != 0)
278*22054f88SWarner Losh 				err(1, "oidfmt");
279*22054f88SWarner Losh 			if ((kind & CTLTYPE) != CTLTYPE_STRING)
280*22054f88SWarner Losh 				errx(1, "string");
281*22054f88SWarner Losh 			l3 = sizeof(val);
282*22054f88SWarner Losh 			if (sysctl(oid, l2, val, &l3, 0, 0) != 0)
283*22054f88SWarner Losh 				err(1, "sysctl");
284*22054f88SWarner Losh 			val[l3] = '\0';
285*22054f88SWarner Losh 			nlat = nitems(latvals);
286*22054f88SWarner Losh 			if (split_u64(val, ",", latvals, &nlat) == 0)
287*22054f88SWarner Losh 				break;
288*22054f88SWarner Losh 			walker = name + strlen(CAM_BASE) + 1;
289*22054f88SWarner Losh 			dev = strsep(&walker, ".");
290*22054f88SWarner Losh 			unit = (int)strtol(strsep(&walker, "."), NULL, 10);
291*22054f88SWarner Losh 			strsep(&walker, ".");
292*22054f88SWarner Losh 			opstr = strsep(&walker, ".");
293*22054f88SWarner Losh 			op = op2num(opstr);
294*22054f88SWarner Losh 			if (op < 0)
295*22054f88SWarner Losh 				break;
296*22054f88SWarner Losh 			update_dev(dev, unit, op, latvals, nlat);
297*22054f88SWarner Losh 		} while (false);
298*22054f88SWarner Losh 
299*22054f88SWarner Losh 		memcpy(qoid + 2, oid, l2 * sizeof(int));
300*22054f88SWarner Losh 		l1 = 2 + l2;
301*22054f88SWarner Losh 	}
302*22054f88SWarner Losh }
303*22054f88SWarner Losh 
304*22054f88SWarner Losh void
closeiolat(WINDOW * w)305*22054f88SWarner Losh closeiolat(WINDOW *w)
306*22054f88SWarner Losh {
307*22054f88SWarner Losh 	if (w == NULL)
308*22054f88SWarner Losh 		return;
309*22054f88SWarner Losh 	wclear(w);
310*22054f88SWarner Losh 	wrefresh(w);
311*22054f88SWarner Losh 	delwin(w);
312*22054f88SWarner Losh }
313*22054f88SWarner Losh 
314*22054f88SWarner Losh static void
doublecmd(const char * cmd,double * v)315*22054f88SWarner Losh doublecmd(const char *cmd, double *v)
316*22054f88SWarner Losh {
317*22054f88SWarner Losh 	const char *p;
318*22054f88SWarner Losh 	double tv;
319*22054f88SWarner Losh 
320*22054f88SWarner Losh 	p = strchr(cmd, '=');
321*22054f88SWarner Losh 	if (p == NULL)
322*22054f88SWarner Losh 		return;	/* XXX Tell the user something? */
323*22054f88SWarner Losh 	if (sscanf(p + 1, "%lf", &tv) != 1)
324*22054f88SWarner Losh 		return;	/* XXX Tell the user something? */
325*22054f88SWarner Losh 	*v = tv;
326*22054f88SWarner Losh }
327*22054f88SWarner Losh 
328*22054f88SWarner Losh int
cmdiolat(const char * cmd __unused,const char * args __unused)329*22054f88SWarner Losh cmdiolat(const char *cmd __unused, const char *args __unused)
330*22054f88SWarner Losh {
331*22054f88SWarner Losh 	fprintf(stderr, "CMD IS '%s'\n\n", cmd);
332*22054f88SWarner Losh 	if (prefix(cmd, "trim"))
333*22054f88SWarner Losh 		flags ^= OP_TRIM_MASK;
334*22054f88SWarner Losh 	else if (prefix(cmd, "read"))
335*22054f88SWarner Losh 		flags ^= OP_READ_MASK;
336*22054f88SWarner Losh 	else if (prefix(cmd, "write"))
337*22054f88SWarner Losh 		flags ^= OP_WRITE_MASK;
338*22054f88SWarner Losh 	else if (prefix(cmd, "color"))
339*22054f88SWarner Losh 		docolor = !docolor;
340*22054f88SWarner Losh 	else if (prefix("high", cmd))
341*22054f88SWarner Losh 		doublecmd(cmd, &high_thresh);
342*22054f88SWarner Losh 	else if (prefix("med", cmd))
343*22054f88SWarner Losh 		doublecmd(cmd, &med_thresh);
344*22054f88SWarner Losh 	else
345*22054f88SWarner Losh 		return (0);
346*22054f88SWarner Losh 	wclear(wnd);
347*22054f88SWarner Losh 	labeliolat();
348*22054f88SWarner Losh 	refresh();
349*22054f88SWarner Losh 	return (1);
350*22054f88SWarner Losh }
351*22054f88SWarner Losh 
352*22054f88SWarner Losh int
initiolat(void)353*22054f88SWarner Losh initiolat(void)
354*22054f88SWarner Losh {
355*22054f88SWarner Losh 	int cam[CTL_MAXNAME];
356*22054f88SWarner Losh 	uint64_t sbt_base;
357*22054f88SWarner Losh 	size_t len = sizeof(sbt_base);
358*22054f88SWarner Losh 
359*22054f88SWarner Losh 	SLIST_INIT(&curlist);
360*22054f88SWarner Losh 
361*22054f88SWarner Losh 	baselat = 1e-3;		/* old default */
362*22054f88SWarner Losh 	if (sysctlbyname(CAM_IOSCHED_BASE, &sbt_base, &len, NULL, 0) == 0)
363*22054f88SWarner Losh 		baselat = sbt_base * 1e-6;	/* Convert to microseconds */
364*22054f88SWarner Losh 
365*22054f88SWarner Losh 	name2oid(CAM_BASE, cam);
366*22054f88SWarner Losh 	walk_sysctl(cam, 2);
367*22054f88SWarner Losh 	return (1);
368*22054f88SWarner Losh }
369*22054f88SWarner Losh 
370*22054f88SWarner Losh void
fetchiolat(void)371*22054f88SWarner Losh fetchiolat(void)
372*22054f88SWarner Losh {
373*22054f88SWarner Losh 	int cam[CTL_MAXNAME];
374*22054f88SWarner Losh 
375*22054f88SWarner Losh 	name2oid(CAM_BASE, cam);
376*22054f88SWarner Losh 	walk_sysctl(cam, 2);
377*22054f88SWarner Losh }
378*22054f88SWarner Losh 
379*22054f88SWarner Losh #define	INSET	10
380*22054f88SWarner Losh 
381*22054f88SWarner Losh void
labeliolat(void)382*22054f88SWarner Losh labeliolat(void)
383*22054f88SWarner Losh {
384*22054f88SWarner Losh 	int _col, ndrives, lpr, row, j;
385*22054f88SWarner Losh 	int regions __unused;
386*22054f88SWarner Losh 	struct iosched_stat *isp;
387*22054f88SWarner Losh 	char tmpstr[32];
388*22054f88SWarner Losh #define COLWIDTH	29
389*22054f88SWarner Losh #define DRIVESPERLINE	((getmaxx(wnd) - 1 - INSET) / COLWIDTH)
390*22054f88SWarner Losh 	ndrives = ndevs; // XXX FILTER XXX
391*22054f88SWarner Losh 	regions = howmany(ndrives, DRIVESPERLINE);
392*22054f88SWarner Losh 	lpr = 2; /* for headers */
393*22054f88SWarner Losh 	for (int i = 0; i < NUM_OPS; i++) {
394*22054f88SWarner Losh 		if (flags & (1 << i))
395*22054f88SWarner Losh 			lpr++;
396*22054f88SWarner Losh 	}
397*22054f88SWarner Losh 	row = 0;
398*22054f88SWarner Losh 	_col = INSET;
399*22054f88SWarner Losh 	j = 2;
400*22054f88SWarner Losh 	if (flags & OP_READ_MASK)
401*22054f88SWarner Losh 		mvwaddstr(wnd, row + j++, 1, "read");
402*22054f88SWarner Losh 	if (flags & OP_WRITE_MASK)
403*22054f88SWarner Losh 		mvwaddstr(wnd, row + j++, 1, "write");
404*22054f88SWarner Losh 	if (flags & OP_TRIM_MASK)
405*22054f88SWarner Losh 		mvwaddstr(wnd, row + j++, 1, "trim");
406*22054f88SWarner Losh 	SLIST_FOREACH(isp, &curlist, link) {
407*22054f88SWarner Losh 		if (_col + COLWIDTH >= getmaxx(wnd) - 1 - INSET) {
408*22054f88SWarner Losh 			_col = INSET;
409*22054f88SWarner Losh 			row += lpr + 1;
410*22054f88SWarner Losh 			if (row > getmaxy(wnd) - 1 - (lpr + 1))
411*22054f88SWarner Losh 				break;
412*22054f88SWarner Losh 			j = 2;
413*22054f88SWarner Losh 			if (flags & OP_READ_MASK)
414*22054f88SWarner Losh 				mvwaddstr(wnd, row + j++, 1, "read");
415*22054f88SWarner Losh 			if (flags & OP_WRITE_MASK)
416*22054f88SWarner Losh 				mvwaddstr(wnd, row + j++, 1, "write");
417*22054f88SWarner Losh 			if (flags & OP_TRIM_MASK)
418*22054f88SWarner Losh 				mvwaddstr(wnd, row + j++, 1, "trim");
419*22054f88SWarner Losh 		}
420*22054f88SWarner Losh 		snprintf(tmpstr, sizeof(tmpstr), "%s%d", isp->dev_name, isp->unit);
421*22054f88SWarner Losh 		mvwaddstr(wnd, row, _col + (COLWIDTH - strlen(tmpstr)) / 2, tmpstr);
422*22054f88SWarner Losh 		mvwaddstr(wnd, row + 1, _col, "   p50    p90    p99  p99.9");
423*22054f88SWarner Losh 		_col += COLWIDTH;
424*22054f88SWarner Losh 	}
425*22054f88SWarner Losh }
426*22054f88SWarner Losh 
427*22054f88SWarner Losh WINDOW *
openiolat(void)428*22054f88SWarner Losh openiolat(void)
429*22054f88SWarner Losh {
430*22054f88SWarner Losh 	return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
431*22054f88SWarner Losh }
432*22054f88SWarner Losh 
433*22054f88SWarner Losh static void
fmt(float f,char * buf,size_t len)434*22054f88SWarner Losh fmt(float f, char *buf, size_t len)
435*22054f88SWarner Losh {
436*22054f88SWarner Losh 	if (isnan(f))
437*22054f88SWarner Losh 		strlcpy(buf, "   -  ", len);
438*22054f88SWarner Losh 	else if (f >= 1000.0)
439*22054f88SWarner Losh 		snprintf(buf, len, "%6d", (int)f);
440*22054f88SWarner Losh 	else if (f >= 100.0)
441*22054f88SWarner Losh 		snprintf(buf, len, "%6.1f", f);
442*22054f88SWarner Losh 	else if (f >= 10.0)
443*22054f88SWarner Losh 		snprintf(buf, len, "%6.2f", f);
444*22054f88SWarner Losh 	else
445*22054f88SWarner Losh 		snprintf(buf, len, "%6.3f", f);
446*22054f88SWarner Losh }
447*22054f88SWarner Losh 
448*22054f88SWarner Losh static void
latout(double lat,int y,int x)449*22054f88SWarner Losh latout(double lat, int y, int x)
450*22054f88SWarner Losh {
451*22054f88SWarner Losh 	int i;
452*22054f88SWarner Losh 	char tmpstr[32];
453*22054f88SWarner Losh 
454*22054f88SWarner Losh 	fmt(lat, tmpstr, sizeof(tmpstr));
455*22054f88SWarner Losh 	if (isnan(lat))
456*22054f88SWarner Losh 		i = 4;
457*22054f88SWarner Losh 	else if (lat > high_thresh)
458*22054f88SWarner Losh 		i = 3;
459*22054f88SWarner Losh 	else if (lat > med_thresh)
460*22054f88SWarner Losh 		i = 2;
461*22054f88SWarner Losh 	else
462*22054f88SWarner Losh 		i = 1;
463*22054f88SWarner Losh 	if (docolor)
464*22054f88SWarner Losh 		wattron(wnd, COLOR_PAIR(i));
465*22054f88SWarner Losh 	mvwaddstr(wnd, y, x, tmpstr);
466*22054f88SWarner Losh 	if (docolor)
467*22054f88SWarner Losh 		wattroff(wnd, COLOR_PAIR(i));
468*22054f88SWarner Losh }
469*22054f88SWarner Losh 
470*22054f88SWarner Losh void
showiolat(void)471*22054f88SWarner Losh showiolat(void)
472*22054f88SWarner Losh {
473*22054f88SWarner Losh 	int _col, ndrives, lpr, row, k;
474*22054f88SWarner Losh 	int regions __unused;
475*22054f88SWarner Losh 	struct iosched_stat *isp;
476*22054f88SWarner Losh 	struct iosched_op_stat *iosp;
477*22054f88SWarner Losh #define COLWIDTH	29
478*22054f88SWarner Losh #define DRIVESPERLINE	((getmaxx(wnd) - 1 - INSET) / COLWIDTH)
479*22054f88SWarner Losh 	ndrives = ndevs; // XXX FILTER XXX
480*22054f88SWarner Losh 	regions = howmany(ndrives, DRIVESPERLINE);
481*22054f88SWarner Losh 	lpr = 2; /* XXX */
482*22054f88SWarner Losh 	for (int i = 0; i < NUM_OPS; i++) {
483*22054f88SWarner Losh 		if (flags & (1 << i))
484*22054f88SWarner Losh 			lpr++;
485*22054f88SWarner Losh 	}
486*22054f88SWarner Losh 	row = 0;
487*22054f88SWarner Losh 	_col = INSET;
488*22054f88SWarner Losh 	SLIST_FOREACH(isp, &curlist, link) {
489*22054f88SWarner Losh 		if (_col + COLWIDTH >= getmaxx(wnd) - 1 - INSET) {
490*22054f88SWarner Losh 			_col = INSET;
491*22054f88SWarner Losh 			row += lpr + 1;
492*22054f88SWarner Losh 			if (row > getmaxy(wnd) - 1 - (lpr + 1))
493*22054f88SWarner Losh 				break;
494*22054f88SWarner Losh 		}
495*22054f88SWarner Losh 		k = 2;
496*22054f88SWarner Losh 		for (int i = 0; i < NUM_OPS; i++) {
497*22054f88SWarner Losh 			uint64_t lats[MAX_LATS];
498*22054f88SWarner Losh 			int nlats;
499*22054f88SWarner Losh 			float p50, p90, p99, p999;
500*22054f88SWarner Losh 
501*22054f88SWarner Losh 			if ((flags & (1 << i)) == 0)
502*22054f88SWarner Losh 				continue;
503*22054f88SWarner Losh 			iosp = &isp->op_stats[i];
504*22054f88SWarner Losh 			nlats = iosp->nlats;
505*22054f88SWarner Losh 			memset(lats, 0, sizeof(lats));
506*22054f88SWarner Losh 			for (int j = 0; j < iosp->nlats; j++)
507*22054f88SWarner Losh 				lats[j] = iosp->lats[j] - iosp->prev_lats[j];
508*22054f88SWarner Losh 			p50 = pest(500, lats, nlats) * E3;
509*22054f88SWarner Losh 			p90 = pest(900, lats, nlats) * E3;
510*22054f88SWarner Losh 			p99 = pest(990, lats, nlats) * E3;
511*22054f88SWarner Losh 			p999 = pest(999, lats, nlats) * E3;
512*22054f88SWarner Losh 			latout(p50, row + k, _col);
513*22054f88SWarner Losh 			latout(p90, row + k, _col + 7);
514*22054f88SWarner Losh 			latout(p99, row + k, _col + 14);
515*22054f88SWarner Losh 			latout(p999, row + k, _col + 21);
516*22054f88SWarner Losh 			k++;
517*22054f88SWarner Losh 		}
518*22054f88SWarner Losh 		_col += COLWIDTH;
519*22054f88SWarner Losh 	}
520*22054f88SWarner Losh }
521