1130f4520SKenneth D. Merry /*- 2*4d846d26SWarner 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/cdefs.h> 42130f4520SKenneth D. Merry __FBSDID("$FreeBSD$"); 43130f4520SKenneth D. Merry 44130f4520SKenneth D. Merry #include <sys/param.h> 45130f4520SKenneth D. Merry #include <sys/callout.h> 461a7f22d9SAlan Somers #include <sys/ioctl.h> 471a7f22d9SAlan Somers #include <sys/queue.h> 481a7f22d9SAlan Somers #include <sys/resource.h> 491a7f22d9SAlan Somers #include <sys/sbuf.h> 501a7f22d9SAlan Somers #include <sys/socket.h> 511a7f22d9SAlan Somers #include <sys/sysctl.h> 521a7f22d9SAlan Somers #include <sys/time.h> 534c163a54SAlan Somers #include <assert.h> 541a7f22d9SAlan Somers #include <bsdxml.h> 551a7f22d9SAlan Somers #include <malloc_np.h> 56130f4520SKenneth D. Merry #include <stdint.h> 57130f4520SKenneth D. Merry #include <stdio.h> 58130f4520SKenneth D. Merry #include <stdlib.h> 59130f4520SKenneth D. Merry #include <unistd.h> 60130f4520SKenneth D. Merry #include <fcntl.h> 611a7f22d9SAlan Somers #include <inttypes.h> 62130f4520SKenneth D. Merry #include <getopt.h> 63130f4520SKenneth D. Merry #include <string.h> 64130f4520SKenneth D. Merry #include <errno.h> 65130f4520SKenneth D. Merry #include <err.h> 66130f4520SKenneth D. Merry #include <ctype.h> 67130f4520SKenneth D. Merry #include <bitstring.h> 68130f4520SKenneth D. Merry #include <cam/scsi/scsi_all.h> 69130f4520SKenneth D. Merry #include <cam/ctl/ctl.h> 70130f4520SKenneth D. Merry #include <cam/ctl/ctl_io.h> 71130f4520SKenneth D. Merry #include <cam/ctl/ctl_scsi_all.h> 72130f4520SKenneth D. Merry #include <cam/ctl/ctl_util.h> 73130f4520SKenneth D. Merry #include <cam/ctl/ctl_backend.h> 74130f4520SKenneth D. Merry #include <cam/ctl/ctl_ioctl.h> 75130f4520SKenneth D. Merry 76130f4520SKenneth D. Merry /* 77bb8f9017SAlexander Motin * The default amount of space we allocate for stats storage space. 78bb8f9017SAlexander Motin * We dynamically allocate more if needed. 79130f4520SKenneth D. Merry */ 80bb8f9017SAlexander Motin #define CTL_STAT_NUM_ITEMS 256 81130f4520SKenneth D. Merry 825e4b2529SBaptiste Daroussin static int ctl_stat_bits; 83130f4520SKenneth D. Merry 841a7f22d9SAlan Somers static const char *ctlstat_opts = "Cc:DPdhjl:n:p:tw:"; 851a7f22d9SAlan Somers static const char *ctlstat_usage = "Usage: ctlstat [-CDPdjht] [-l lunnum]" 86130f4520SKenneth D. Merry "[-c count] [-n numdevs] [-w wait]\n"; 87130f4520SKenneth D. Merry 88130f4520SKenneth D. Merry struct ctl_cpu_stats { 89130f4520SKenneth D. Merry uint64_t user; 90130f4520SKenneth D. Merry uint64_t nice; 91130f4520SKenneth D. Merry uint64_t system; 92130f4520SKenneth D. Merry uint64_t intr; 93130f4520SKenneth D. Merry uint64_t idle; 94130f4520SKenneth D. Merry }; 95130f4520SKenneth D. Merry 96130f4520SKenneth D. Merry typedef enum { 97130f4520SKenneth D. Merry CTLSTAT_MODE_STANDARD, 98130f4520SKenneth D. Merry CTLSTAT_MODE_DUMP, 99130f4520SKenneth D. Merry CTLSTAT_MODE_JSON, 1001a7f22d9SAlan Somers CTLSTAT_MODE_PROMETHEUS, 101130f4520SKenneth D. Merry } ctlstat_mode_types; 102130f4520SKenneth D. Merry 103130f4520SKenneth D. Merry #define CTLSTAT_FLAG_CPU (1 << 0) 104130f4520SKenneth D. Merry #define CTLSTAT_FLAG_HEADER (1 << 1) 105130f4520SKenneth D. Merry #define CTLSTAT_FLAG_FIRST_RUN (1 << 2) 106130f4520SKenneth D. Merry #define CTLSTAT_FLAG_TOTALS (1 << 3) 107130f4520SKenneth D. Merry #define CTLSTAT_FLAG_DMA_TIME (1 << 4) 108bb8f9017SAlexander Motin #define CTLSTAT_FLAG_TIME_VALID (1 << 5) 109bb8f9017SAlexander Motin #define CTLSTAT_FLAG_MASK (1 << 6) 110bb8f9017SAlexander Motin #define CTLSTAT_FLAG_LUNS (1 << 7) 111bb8f9017SAlexander Motin #define CTLSTAT_FLAG_PORTS (1 << 8) 112130f4520SKenneth D. Merry #define F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU) 113130f4520SKenneth D. Merry #define F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER) 114130f4520SKenneth D. Merry #define F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN) 115130f4520SKenneth D. Merry #define F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS) 116130f4520SKenneth D. Merry #define F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME) 117bb8f9017SAlexander Motin #define F_TIMEVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_TIME_VALID) 118bb8f9017SAlexander Motin #define F_MASK(ctx) ((ctx)->flags & CTLSTAT_FLAG_MASK) 119bb8f9017SAlexander Motin #define F_LUNS(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUNS) 120bb8f9017SAlexander Motin #define F_PORTS(ctx) ((ctx)->flags & CTLSTAT_FLAG_PORTS) 121130f4520SKenneth D. Merry 122130f4520SKenneth D. Merry struct ctlstat_context { 123130f4520SKenneth D. Merry ctlstat_mode_types mode; 124130f4520SKenneth D. Merry int flags; 125bb8f9017SAlexander Motin struct ctl_io_stats *cur_stats, *prev_stats; 126bb8f9017SAlexander Motin struct ctl_io_stats cur_total_stats[3], prev_total_stats[3]; 127130f4520SKenneth D. Merry struct timespec cur_time, prev_time; 128130f4520SKenneth D. Merry struct ctl_cpu_stats cur_cpu, prev_cpu; 129130f4520SKenneth D. Merry uint64_t cur_total_jiffies, prev_total_jiffies; 130130f4520SKenneth D. Merry uint64_t cur_idle, prev_idle; 1315e4b2529SBaptiste Daroussin bitstr_t *item_mask; 132bb8f9017SAlexander Motin int cur_items, prev_items; 133bb8f9017SAlexander Motin int cur_alloc, prev_alloc; 134130f4520SKenneth D. Merry int numdevs; 135130f4520SKenneth D. Merry int header_interval; 136130f4520SKenneth D. Merry }; 137130f4520SKenneth D. Merry 1381a7f22d9SAlan Somers struct cctl_portlist_data { 1391a7f22d9SAlan Somers int level; 1401a7f22d9SAlan Somers struct sbuf *cur_sb[32]; 1414c163a54SAlan Somers int id; 1421a7f22d9SAlan Somers int lun; 1431a7f22d9SAlan Somers int ntargets; 1441a7f22d9SAlan Somers char *target; 1451a7f22d9SAlan Somers char **targets; 1461a7f22d9SAlan Somers }; 1471a7f22d9SAlan Somers 148130f4520SKenneth D. Merry #ifndef min 149130f4520SKenneth D. Merry #define min(x,y) (((x) < (y)) ? (x) : (y)) 150130f4520SKenneth D. Merry #endif 151130f4520SKenneth D. Merry 152130f4520SKenneth D. Merry static void usage(int error); 153bb8f9017SAlexander Motin static int getstats(int fd, int *alloc_items, int *num_items, 1544c163a54SAlan Somers struct ctl_io_stats **xstats, struct timespec *cur_time, int *time_valid, 1554c163a54SAlan Somers bool ports); 156130f4520SKenneth D. Merry static int getcpu(struct ctl_cpu_stats *cpu_stats); 157bb8f9017SAlexander Motin static void compute_stats(struct ctl_io_stats *cur_stats, 158bb8f9017SAlexander Motin struct ctl_io_stats *prev_stats, 159130f4520SKenneth D. Merry long double etime, long double *mbsec, 160130f4520SKenneth D. Merry long double *kb_per_transfer, 161130f4520SKenneth D. Merry long double *transfers_per_second, 162130f4520SKenneth D. Merry long double *ms_per_transfer, 163130f4520SKenneth D. Merry long double *ms_per_dma, 164130f4520SKenneth D. Merry long double *dmas_per_second); 165130f4520SKenneth D. Merry 166130f4520SKenneth D. Merry static void 167130f4520SKenneth D. Merry usage(int error) 168130f4520SKenneth D. Merry { 16933d35bebSKenneth D. Merry fputs(ctlstat_usage, error ? stderr : stdout); 170130f4520SKenneth D. Merry } 171130f4520SKenneth D. Merry 172130f4520SKenneth D. Merry static int 173bb8f9017SAlexander Motin getstats(int fd, int *alloc_items, int *num_items, struct ctl_io_stats **stats, 1744c163a54SAlan Somers struct timespec *cur_time, int *flags, bool ports) 175130f4520SKenneth D. Merry { 176bb8f9017SAlexander Motin struct ctl_get_io_stats get_stats; 177bb8f9017SAlexander Motin int more_space_count = 0; 178130f4520SKenneth D. Merry 179bb8f9017SAlexander Motin if (*alloc_items == 0) 180bb8f9017SAlexander Motin *alloc_items = CTL_STAT_NUM_ITEMS; 181130f4520SKenneth D. Merry retry: 182bb8f9017SAlexander Motin if (*stats == NULL) 183bb8f9017SAlexander Motin *stats = malloc(sizeof(**stats) * *alloc_items); 184130f4520SKenneth D. Merry 185bb8f9017SAlexander Motin memset(&get_stats, 0, sizeof(get_stats)); 186bb8f9017SAlexander Motin get_stats.alloc_len = *alloc_items * sizeof(**stats); 187bb8f9017SAlexander Motin memset(*stats, 0, get_stats.alloc_len); 188bb8f9017SAlexander Motin get_stats.stats = *stats; 189130f4520SKenneth D. Merry 1904c163a54SAlan Somers if (ioctl(fd, ports ? CTL_GET_PORT_STATS : CTL_GET_LUN_STATS, 1914c163a54SAlan Somers &get_stats) == -1) 192bb8f9017SAlexander Motin err(1, "CTL_GET_*_STATS ioctl returned error"); 193130f4520SKenneth D. Merry 194bb8f9017SAlexander Motin switch (get_stats.status) { 195130f4520SKenneth D. Merry case CTL_SS_OK: 196130f4520SKenneth D. Merry break; 197130f4520SKenneth D. Merry case CTL_SS_ERROR: 198bb8f9017SAlexander Motin err(1, "CTL_GET_*_STATS ioctl returned CTL_SS_ERROR"); 199130f4520SKenneth D. Merry break; 200130f4520SKenneth D. Merry case CTL_SS_NEED_MORE_SPACE: 201bb8f9017SAlexander Motin if (more_space_count >= 2) 202bb8f9017SAlexander Motin errx(1, "CTL_GET_*_STATS returned NEED_MORE_SPACE again"); 203bb8f9017SAlexander Motin *alloc_items = get_stats.num_items * 5 / 4; 204bb8f9017SAlexander Motin free(*stats); 205bb8f9017SAlexander Motin *stats = NULL; 206130f4520SKenneth D. Merry more_space_count++; 207130f4520SKenneth D. Merry goto retry; 208130f4520SKenneth D. Merry break; /* NOTREACHED */ 209130f4520SKenneth D. Merry default: 210bb8f9017SAlexander Motin errx(1, "CTL_GET_*_STATS ioctl returned unknown status %d", 211bb8f9017SAlexander Motin get_stats.status); 212130f4520SKenneth D. Merry break; 213130f4520SKenneth D. Merry } 214130f4520SKenneth D. Merry 215bb8f9017SAlexander Motin *num_items = get_stats.fill_len / sizeof(**stats); 216bb8f9017SAlexander Motin cur_time->tv_sec = get_stats.timestamp.tv_sec; 217bb8f9017SAlexander Motin cur_time->tv_nsec = get_stats.timestamp.tv_nsec; 218bb8f9017SAlexander Motin if (get_stats.flags & CTL_STATS_FLAG_TIME_VALID) 219bb8f9017SAlexander Motin *flags |= CTLSTAT_FLAG_TIME_VALID; 220130f4520SKenneth D. Merry else 221bb8f9017SAlexander Motin *flags &= ~CTLSTAT_FLAG_TIME_VALID; 222130f4520SKenneth D. Merry 223130f4520SKenneth D. Merry return (0); 224130f4520SKenneth D. Merry } 225130f4520SKenneth D. Merry 226130f4520SKenneth D. Merry static int 227130f4520SKenneth D. Merry getcpu(struct ctl_cpu_stats *cpu_stats) 228130f4520SKenneth D. Merry { 229130f4520SKenneth D. Merry long cp_time[CPUSTATES]; 230130f4520SKenneth D. Merry size_t cplen; 231130f4520SKenneth D. Merry 232130f4520SKenneth D. Merry cplen = sizeof(cp_time); 233130f4520SKenneth D. Merry 234130f4520SKenneth D. Merry if (sysctlbyname("kern.cp_time", &cp_time, &cplen, NULL, 0) == -1) { 235130f4520SKenneth D. Merry warn("sysctlbyname(kern.cp_time...) failed"); 236130f4520SKenneth D. Merry return (1); 237130f4520SKenneth D. Merry } 238130f4520SKenneth D. Merry 239130f4520SKenneth D. Merry cpu_stats->user = cp_time[CP_USER]; 240130f4520SKenneth D. Merry cpu_stats->nice = cp_time[CP_NICE]; 241130f4520SKenneth D. Merry cpu_stats->system = cp_time[CP_SYS]; 242130f4520SKenneth D. Merry cpu_stats->intr = cp_time[CP_INTR]; 243130f4520SKenneth D. Merry cpu_stats->idle = cp_time[CP_IDLE]; 244130f4520SKenneth D. Merry 245130f4520SKenneth D. Merry return (0); 246130f4520SKenneth D. Merry } 247130f4520SKenneth D. Merry 248130f4520SKenneth D. Merry static void 249bb8f9017SAlexander Motin compute_stats(struct ctl_io_stats *cur_stats, 250bb8f9017SAlexander Motin struct ctl_io_stats *prev_stats, long double etime, 251130f4520SKenneth D. Merry long double *mbsec, long double *kb_per_transfer, 252130f4520SKenneth D. Merry long double *transfers_per_second, long double *ms_per_transfer, 253130f4520SKenneth D. Merry long double *ms_per_dma, long double *dmas_per_second) 254130f4520SKenneth D. Merry { 255130f4520SKenneth D. Merry uint64_t total_bytes = 0, total_operations = 0, total_dmas = 0; 256130f4520SKenneth D. Merry struct bintime total_time_bt, total_dma_bt; 257130f4520SKenneth D. Merry struct timespec total_time_ts, total_dma_ts; 258130f4520SKenneth D. Merry int i; 259130f4520SKenneth D. Merry 260130f4520SKenneth D. Merry bzero(&total_time_bt, sizeof(total_time_bt)); 261130f4520SKenneth D. Merry bzero(&total_dma_bt, sizeof(total_dma_bt)); 262130f4520SKenneth D. Merry bzero(&total_time_ts, sizeof(total_time_ts)); 263130f4520SKenneth D. Merry bzero(&total_dma_ts, sizeof(total_dma_ts)); 264130f4520SKenneth D. Merry for (i = 0; i < CTL_STATS_NUM_TYPES; i++) { 265bb8f9017SAlexander Motin total_bytes += cur_stats->bytes[i]; 266bb8f9017SAlexander Motin total_operations += cur_stats->operations[i]; 267bb8f9017SAlexander Motin total_dmas += cur_stats->dmas[i]; 268bb8f9017SAlexander Motin bintime_add(&total_time_bt, &cur_stats->time[i]); 269bb8f9017SAlexander Motin bintime_add(&total_dma_bt, &cur_stats->dma_time[i]); 270130f4520SKenneth D. Merry if (prev_stats != NULL) { 271bb8f9017SAlexander Motin total_bytes -= prev_stats->bytes[i]; 272bb8f9017SAlexander Motin total_operations -= prev_stats->operations[i]; 273bb8f9017SAlexander Motin total_dmas -= prev_stats->dmas[i]; 274bb8f9017SAlexander Motin bintime_sub(&total_time_bt, &prev_stats->time[i]); 275bb8f9017SAlexander Motin bintime_sub(&total_dma_bt, &prev_stats->dma_time[i]); 276130f4520SKenneth D. Merry } 277130f4520SKenneth D. Merry } 278130f4520SKenneth D. Merry 279130f4520SKenneth D. Merry *mbsec = total_bytes; 280130f4520SKenneth D. Merry *mbsec /= 1024 * 1024; 281130f4520SKenneth D. Merry if (etime > 0.0) 282130f4520SKenneth D. Merry *mbsec /= etime; 283130f4520SKenneth D. Merry else 284130f4520SKenneth D. Merry *mbsec = 0; 285130f4520SKenneth D. Merry *kb_per_transfer = total_bytes; 286130f4520SKenneth D. Merry *kb_per_transfer /= 1024; 287130f4520SKenneth D. Merry if (total_operations > 0) 288130f4520SKenneth D. Merry *kb_per_transfer /= total_operations; 289130f4520SKenneth D. Merry else 290130f4520SKenneth D. Merry *kb_per_transfer = 0; 291130f4520SKenneth D. Merry *transfers_per_second = total_operations; 292130f4520SKenneth D. Merry *dmas_per_second = total_dmas; 293130f4520SKenneth D. Merry if (etime > 0.0) { 294130f4520SKenneth D. Merry *transfers_per_second /= etime; 295130f4520SKenneth D. Merry *dmas_per_second /= etime; 296130f4520SKenneth D. Merry } else { 297130f4520SKenneth D. Merry *transfers_per_second = 0; 298130f4520SKenneth D. Merry *dmas_per_second = 0; 299130f4520SKenneth D. Merry } 300130f4520SKenneth D. Merry 301130f4520SKenneth D. Merry bintime2timespec(&total_time_bt, &total_time_ts); 302130f4520SKenneth D. Merry bintime2timespec(&total_dma_bt, &total_dma_ts); 303130f4520SKenneth D. Merry if (total_operations > 0) { 304130f4520SKenneth D. Merry /* 305130f4520SKenneth D. Merry * Convert the timespec to milliseconds. 306130f4520SKenneth D. Merry */ 307130f4520SKenneth D. Merry *ms_per_transfer = total_time_ts.tv_sec * 1000; 308130f4520SKenneth D. Merry *ms_per_transfer += total_time_ts.tv_nsec / 1000000; 309130f4520SKenneth D. Merry *ms_per_transfer /= total_operations; 310130f4520SKenneth D. Merry } else 311130f4520SKenneth D. Merry *ms_per_transfer = 0; 312130f4520SKenneth D. Merry 313130f4520SKenneth D. Merry if (total_dmas > 0) { 314130f4520SKenneth D. Merry /* 315130f4520SKenneth D. Merry * Convert the timespec to milliseconds. 316130f4520SKenneth D. Merry */ 317130f4520SKenneth D. Merry *ms_per_dma = total_dma_ts.tv_sec * 1000; 318130f4520SKenneth D. Merry *ms_per_dma += total_dma_ts.tv_nsec / 1000000; 319130f4520SKenneth D. Merry *ms_per_dma /= total_dmas; 320130f4520SKenneth D. Merry } else 321130f4520SKenneth D. Merry *ms_per_dma = 0; 322130f4520SKenneth D. Merry } 323130f4520SKenneth D. Merry 324130f4520SKenneth D. Merry /* The dump_stats() and json_stats() functions perform essentially the same 325130f4520SKenneth D. Merry * purpose, but dump the statistics in different formats. JSON is more 326130f4520SKenneth D. Merry * conducive to programming, however. 327130f4520SKenneth D. Merry */ 328130f4520SKenneth D. Merry 329924233ebSAlexander Motin #define PRINT_BINTIME(bt) \ 330924233ebSAlexander Motin printf("%jd.%06ju", (intmax_t)(bt).sec, \ 3312eb293b9SAlexander Motin (uintmax_t)(((bt).frac >> 32) * 1000000 >> 32)) 332bf70beceSEd Schouten static const char *iotypes[] = {"NO IO", "READ", "WRITE"}; 333130f4520SKenneth D. Merry 334130f4520SKenneth D. Merry static void 335bb8f9017SAlexander Motin ctlstat_dump(struct ctlstat_context *ctx) 336bb8f9017SAlexander Motin { 3375be4479bSAlexander Motin int iotype, i, n; 338bb8f9017SAlexander Motin struct ctl_io_stats *stats = ctx->cur_stats; 339130f4520SKenneth D. Merry 3405be4479bSAlexander Motin for (i = n = 0; i < ctx->cur_items;i++) { 3415be4479bSAlexander Motin if (F_MASK(ctx) && bit_test(ctx->item_mask, 3425be4479bSAlexander Motin (int)stats[i].item) == 0) 34361639a0aSAlexander Motin continue; 344bb8f9017SAlexander Motin printf("%s %d\n", F_PORTS(ctx) ? "port" : "lun", stats[i].item); 345bb8f9017SAlexander Motin for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { 346bb8f9017SAlexander Motin printf(" io type %d (%s)\n", iotype, iotypes[iotype]); 347130f4520SKenneth D. Merry printf(" bytes %ju\n", (uintmax_t) 348bb8f9017SAlexander Motin stats[i].bytes[iotype]); 349130f4520SKenneth D. Merry printf(" operations %ju\n", (uintmax_t) 350bb8f9017SAlexander Motin stats[i].operations[iotype]); 351bb8f9017SAlexander Motin printf(" dmas %ju\n", (uintmax_t) 352bb8f9017SAlexander Motin stats[i].dmas[iotype]); 353924233ebSAlexander Motin printf(" io time "); 354924233ebSAlexander Motin PRINT_BINTIME(stats[i].time[iotype]); 355924233ebSAlexander Motin printf("\n dma time "); 356924233ebSAlexander Motin PRINT_BINTIME(stats[i].dma_time[iotype]); 357924233ebSAlexander Motin printf("\n"); 358130f4520SKenneth D. Merry } 3595be4479bSAlexander Motin if (++n >= ctx->numdevs) 3605be4479bSAlexander Motin break; 361130f4520SKenneth D. Merry } 362130f4520SKenneth D. Merry } 363130f4520SKenneth D. Merry 364130f4520SKenneth D. Merry static void 365130f4520SKenneth D. Merry ctlstat_json(struct ctlstat_context *ctx) { 3665be4479bSAlexander Motin int iotype, i, n; 367bb8f9017SAlexander Motin struct ctl_io_stats *stats = ctx->cur_stats; 368130f4520SKenneth D. Merry 369bb8f9017SAlexander Motin printf("{\"%s\":[", F_PORTS(ctx) ? "ports" : "luns"); 3705be4479bSAlexander Motin for (i = n = 0; i < ctx->cur_items; i++) { 3715be4479bSAlexander Motin if (F_MASK(ctx) && bit_test(ctx->item_mask, 3725be4479bSAlexander Motin (int)stats[i].item) == 0) 37361639a0aSAlexander Motin continue; 374130f4520SKenneth D. Merry printf("{\"num\":%d,\"io\":[", 375bb8f9017SAlexander Motin stats[i].item); 376bb8f9017SAlexander Motin for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { 377130f4520SKenneth D. Merry printf("{\"type\":\"%s\",", iotypes[iotype]); 378924233ebSAlexander Motin printf("\"bytes\":%ju,", (uintmax_t) 379924233ebSAlexander Motin stats[i].bytes[iotype]); 380924233ebSAlexander Motin printf("\"operations\":%ju,", (uintmax_t) 381924233ebSAlexander Motin stats[i].operations[iotype]); 382924233ebSAlexander Motin printf("\"dmas\":%ju,", (uintmax_t) 383bb8f9017SAlexander Motin stats[i].dmas[iotype]); 384924233ebSAlexander Motin printf("\"io time\":"); 385924233ebSAlexander Motin PRINT_BINTIME(stats[i].time[iotype]); 386924233ebSAlexander Motin printf(",\"dma time\":"); 387924233ebSAlexander Motin PRINT_BINTIME(stats[i].dma_time[iotype]); 388924233ebSAlexander Motin printf("}"); 389130f4520SKenneth D. Merry if (iotype < (CTL_STATS_NUM_TYPES - 1)) 390130f4520SKenneth D. Merry printf(","); /* continue io array */ 391130f4520SKenneth D. Merry } 392bb8f9017SAlexander Motin printf("]}"); 3935be4479bSAlexander Motin if (++n >= ctx->numdevs) 3945be4479bSAlexander Motin break; 395bb8f9017SAlexander Motin if (i < (ctx->cur_items - 1)) 396130f4520SKenneth D. Merry printf(","); /* continue lun array */ 397130f4520SKenneth D. Merry } 398bb8f9017SAlexander Motin printf("]}"); 399130f4520SKenneth D. Merry } 400130f4520SKenneth D. Merry 4014c163a54SAlan Somers #define CTLSTAT_PROMETHEUS_LOOP(field, collector) \ 4021a7f22d9SAlan Somers for (i = n = 0; i < ctx->cur_items; i++) { \ 4031a7f22d9SAlan Somers if (F_MASK(ctx) && bit_test(ctx->item_mask, \ 4041a7f22d9SAlan Somers (int)stats[i].item) == 0) \ 4051a7f22d9SAlan Somers continue; \ 4061a7f22d9SAlan Somers for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { \ 4074c163a54SAlan Somers int idx = stats[i].item; \ 4084c163a54SAlan Somers /* \ 4094c163a54SAlan Somers * Note that Prometheus considers a label value of "" \ 4104c163a54SAlan Somers * to be the same as no label at all \ 4114c163a54SAlan Somers */ \ 4124c163a54SAlan Somers const char *target = ""; \ 4134c163a54SAlan Somers if (strcmp(collector, "port") == 0 && \ 4144c163a54SAlan Somers targdata.targets[idx] != NULL) \ 4154c163a54SAlan Somers { \ 4164c163a54SAlan Somers target = targdata.targets[idx]; \ 4174c163a54SAlan Somers } \ 4184c163a54SAlan Somers printf("iscsi_%s_" #field "{" \ 4194c163a54SAlan Somers "%s=\"%u\",target=\"%s\",type=\"%s\"} %" PRIu64 \ 4201a7f22d9SAlan Somers "\n", \ 4214c163a54SAlan Somers collector, collector, \ 4224c163a54SAlan Somers idx, target, iotypes[iotype], \ 4231a7f22d9SAlan Somers stats[i].field[iotype]); \ 4241a7f22d9SAlan Somers } \ 4251a7f22d9SAlan Somers } \ 4261a7f22d9SAlan Somers 4274c163a54SAlan Somers #define CTLSTAT_PROMETHEUS_TIMELOOP(field, collector) \ 4281a7f22d9SAlan Somers for (i = n = 0; i < ctx->cur_items; i++) { \ 4291a7f22d9SAlan Somers if (F_MASK(ctx) && bit_test(ctx->item_mask, \ 4301a7f22d9SAlan Somers (int)stats[i].item) == 0) \ 4311a7f22d9SAlan Somers continue; \ 4321a7f22d9SAlan Somers for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { \ 4331a7f22d9SAlan Somers uint64_t us; \ 4341a7f22d9SAlan Somers struct timespec ts; \ 4354c163a54SAlan Somers int idx = stats[i].item; \ 4364c163a54SAlan Somers /* \ 4374c163a54SAlan Somers * Note that Prometheus considers a label value of "" \ 4384c163a54SAlan Somers * to be the same as no label at all \ 4394c163a54SAlan Somers */ \ 4404c163a54SAlan Somers const char *target = ""; \ 4414c163a54SAlan Somers if (strcmp(collector, "port") == 0 && \ 4424c163a54SAlan Somers targdata.targets[idx] != NULL) \ 4434c163a54SAlan Somers { \ 4444c163a54SAlan Somers target = targdata.targets[idx]; \ 4454c163a54SAlan Somers } \ 4461a7f22d9SAlan Somers bintime2timespec(&stats[i].field[iotype], &ts); \ 4471a7f22d9SAlan Somers us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000; \ 4484c163a54SAlan Somers printf("iscsi_%s_" #field "{" \ 4494c163a54SAlan Somers "%s=\"%u\",target=\"%s\",type=\"%s\"} %" PRIu64 \ 4501a7f22d9SAlan Somers "\n", \ 4514c163a54SAlan Somers collector, collector, \ 4524c163a54SAlan Somers idx, target, iotypes[iotype], us); \ 4531a7f22d9SAlan Somers } \ 4541a7f22d9SAlan Somers } \ 4551a7f22d9SAlan Somers 4561a7f22d9SAlan Somers static void 4574c163a54SAlan Somers cctl_start_pelement(void *user_data, const char *name, const char **attr) 4581a7f22d9SAlan Somers { 4591a7f22d9SAlan Somers struct cctl_portlist_data* targdata = user_data; 4601a7f22d9SAlan Somers 4611a7f22d9SAlan Somers targdata->level++; 4621a7f22d9SAlan Somers if ((u_int)targdata->level >= (sizeof(targdata->cur_sb) / 4631a7f22d9SAlan Somers sizeof(targdata->cur_sb[0]))) 4641a7f22d9SAlan Somers errx(1, "%s: too many nesting levels, %zd max", __func__, 4651a7f22d9SAlan Somers sizeof(targdata->cur_sb) / sizeof(targdata->cur_sb[0])); 4661a7f22d9SAlan Somers 4671a7f22d9SAlan Somers targdata->cur_sb[targdata->level] = sbuf_new_auto(); 4681a7f22d9SAlan Somers if (targdata->cur_sb[targdata->level] == NULL) 4691a7f22d9SAlan Somers err(1, "%s: Unable to allocate sbuf", __func__); 4701a7f22d9SAlan Somers 4711a7f22d9SAlan Somers if (strcmp(name, "targ_port") == 0) { 4724c163a54SAlan Somers int i = 0; 4734c163a54SAlan Somers 4741a7f22d9SAlan Somers targdata->lun = -1; 4754c163a54SAlan Somers targdata->id = -1; 4761a7f22d9SAlan Somers free(targdata->target); 4771a7f22d9SAlan Somers targdata->target = NULL; 4784c163a54SAlan Somers while (attr[i]) { 4794c163a54SAlan Somers if (strcmp(attr[i], "id") == 0) { 4804c163a54SAlan Somers /* 4814c163a54SAlan Somers * Well-formed XML always pairs keys with 4824c163a54SAlan Somers * values in attr 4834c163a54SAlan Somers */ 4844c163a54SAlan Somers assert(attr[i + 1]); 4854c163a54SAlan Somers targdata->id = atoi(attr[i + 1]); 4864c163a54SAlan Somers } 4874c163a54SAlan Somers i += 2; 4884c163a54SAlan Somers } 4894c163a54SAlan Somers 4901a7f22d9SAlan Somers } 4911a7f22d9SAlan Somers } 4921a7f22d9SAlan Somers 4931a7f22d9SAlan Somers static void 4941a7f22d9SAlan Somers cctl_char_phandler(void *user_data, const XML_Char *str, int len) 4951a7f22d9SAlan Somers { 4961a7f22d9SAlan Somers struct cctl_portlist_data *targdata = user_data; 4971a7f22d9SAlan Somers 4981a7f22d9SAlan Somers sbuf_bcat(targdata->cur_sb[targdata->level], str, len); 4991a7f22d9SAlan Somers } 5001a7f22d9SAlan Somers 5011a7f22d9SAlan Somers static void 5021a7f22d9SAlan Somers cctl_end_pelement(void *user_data, const char *name) 5031a7f22d9SAlan Somers { 5041a7f22d9SAlan Somers struct cctl_portlist_data* targdata = user_data; 5051a7f22d9SAlan Somers char *str; 5061a7f22d9SAlan Somers 5071a7f22d9SAlan Somers if (targdata->cur_sb[targdata->level] == NULL) 5081a7f22d9SAlan Somers errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, 5091a7f22d9SAlan Somers targdata->level, name); 5101a7f22d9SAlan Somers 5111a7f22d9SAlan Somers if (sbuf_finish(targdata->cur_sb[targdata->level]) != 0) 5121a7f22d9SAlan Somers err(1, "%s: sbuf_finish", __func__); 5131a7f22d9SAlan Somers str = strdup(sbuf_data(targdata->cur_sb[targdata->level])); 5141a7f22d9SAlan Somers if (str == NULL) 5151a7f22d9SAlan Somers err(1, "%s can't allocate %zd bytes for string", __func__, 5161a7f22d9SAlan Somers sbuf_len(targdata->cur_sb[targdata->level])); 5171a7f22d9SAlan Somers 5181a7f22d9SAlan Somers sbuf_delete(targdata->cur_sb[targdata->level]); 5191a7f22d9SAlan Somers targdata->cur_sb[targdata->level] = NULL; 5201a7f22d9SAlan Somers targdata->level--; 5211a7f22d9SAlan Somers 5221a7f22d9SAlan Somers if (strcmp(name, "target") == 0) { 5231a7f22d9SAlan Somers free(targdata->target); 5241a7f22d9SAlan Somers targdata->target = str; 5251a7f22d9SAlan Somers } else if (strcmp(name, "targ_port") == 0) { 5264c163a54SAlan Somers if (targdata->id >= 0 && targdata->target != NULL) { 5274c163a54SAlan Somers if (targdata->id >= targdata->ntargets) { 5281a7f22d9SAlan Somers /* 5291a7f22d9SAlan Somers * This can happen for example if there are 5304c163a54SAlan Somers * targets with no LUNs. 5311a7f22d9SAlan Somers */ 5321a7f22d9SAlan Somers targdata->ntargets = MAX(targdata->ntargets * 2, 5334c163a54SAlan Somers targdata->id + 1); 5341a7f22d9SAlan Somers size_t newsize = targdata->ntargets * 5351a7f22d9SAlan Somers sizeof(char*); 5361a7f22d9SAlan Somers targdata->targets = rallocx(targdata->targets, 5371a7f22d9SAlan Somers newsize, MALLOCX_ZERO); 5381a7f22d9SAlan Somers } 5394c163a54SAlan Somers free(targdata->targets[targdata->id]); 5404c163a54SAlan Somers targdata->targets[targdata->id] = targdata->target; 5411a7f22d9SAlan Somers targdata->target = NULL; 5421a7f22d9SAlan Somers } 5431a7f22d9SAlan Somers free(str); 5441a7f22d9SAlan Somers } else { 5451a7f22d9SAlan Somers free(str); 5461a7f22d9SAlan Somers } 5471a7f22d9SAlan Somers } 5481a7f22d9SAlan Somers 5491a7f22d9SAlan Somers static void 5504c163a54SAlan Somers ctlstat_prometheus(int fd, struct ctlstat_context *ctx, bool ports) { 5511a7f22d9SAlan Somers struct ctl_io_stats *stats = ctx->cur_stats; 5521a7f22d9SAlan Somers struct ctl_lun_list list; 5531a7f22d9SAlan Somers struct cctl_portlist_data targdata; 5541a7f22d9SAlan Somers XML_Parser parser; 5551a7f22d9SAlan Somers char *port_str = NULL; 5561a7f22d9SAlan Somers int iotype, i, n, retval; 5571a7f22d9SAlan Somers int port_len = 4096; 5584c163a54SAlan Somers const char *collector; 5591a7f22d9SAlan Somers 5601a7f22d9SAlan Somers bzero(&targdata, sizeof(targdata)); 5611a7f22d9SAlan Somers targdata.ntargets = ctx->cur_items; 5621a7f22d9SAlan Somers targdata.targets = calloc(targdata.ntargets, sizeof(char*)); 5631a7f22d9SAlan Somers retry: 5641a7f22d9SAlan Somers port_str = (char *)realloc(port_str, port_len); 5651a7f22d9SAlan Somers bzero(&list, sizeof(list)); 5661a7f22d9SAlan Somers list.alloc_len = port_len; 5671a7f22d9SAlan Somers list.status = CTL_LUN_LIST_NONE; 5681a7f22d9SAlan Somers list.lun_xml = port_str; 5691a7f22d9SAlan Somers if (ioctl(fd, CTL_PORT_LIST, &list) == -1) 5701a7f22d9SAlan Somers err(1, "%s: error issuing CTL_PORT_LIST ioctl", __func__); 5711a7f22d9SAlan Somers if (list.status == CTL_LUN_LIST_ERROR) { 5721a7f22d9SAlan Somers warnx("%s: error returned from CTL_PORT_LIST ioctl:\n%s", 5731a7f22d9SAlan Somers __func__, list.error_str); 5741a7f22d9SAlan Somers } else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { 5751a7f22d9SAlan Somers port_len <<= 1; 5761a7f22d9SAlan Somers goto retry; 5771a7f22d9SAlan Somers } 5781a7f22d9SAlan Somers 5791a7f22d9SAlan Somers parser = XML_ParserCreate(NULL); 5801a7f22d9SAlan Somers if (parser == NULL) 5811a7f22d9SAlan Somers err(1, "%s: Unable to create XML parser", __func__); 5821a7f22d9SAlan Somers XML_SetUserData(parser, &targdata); 5831a7f22d9SAlan Somers XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement); 5841a7f22d9SAlan Somers XML_SetCharacterDataHandler(parser, cctl_char_phandler); 5851a7f22d9SAlan Somers 5861a7f22d9SAlan Somers retval = XML_Parse(parser, port_str, strlen(port_str), 1); 5871a7f22d9SAlan Somers if (retval != 1) { 5881a7f22d9SAlan Somers errx(1, "%s: Unable to parse XML: Error %d", __func__, 5891a7f22d9SAlan Somers XML_GetErrorCode(parser)); 5901a7f22d9SAlan Somers } 5911a7f22d9SAlan Somers XML_ParserFree(parser); 5921a7f22d9SAlan Somers 5934c163a54SAlan Somers collector = ports ? "port" : "lun"; 5941a7f22d9SAlan Somers 5954c163a54SAlan Somers printf("# HELP iscsi_%s_bytes Number of bytes\n" 5964c163a54SAlan Somers "# TYPE iscsi_%s_bytes counter\n", collector, collector); 5974c163a54SAlan Somers CTLSTAT_PROMETHEUS_LOOP(bytes, collector); 5984c163a54SAlan Somers printf("# HELP iscsi_%s_dmas Number of DMA\n" 5994c163a54SAlan Somers "# TYPE iscsi_%s_dmas counter\n", collector, collector); 6004c163a54SAlan Somers CTLSTAT_PROMETHEUS_LOOP(dmas, collector); 6014c163a54SAlan Somers printf("# HELP iscsi_%s_operations Number of operations\n" 6024c163a54SAlan Somers "# TYPE iscsi_%s_operations counter\n", collector, collector); 6034c163a54SAlan Somers CTLSTAT_PROMETHEUS_LOOP(operations, collector); 6044c163a54SAlan Somers printf("# HELP iscsi_%s_time Cumulative operation time in us\n" 6054c163a54SAlan Somers "# TYPE iscsi_%s_time counter\n", collector, collector); 6064c163a54SAlan Somers CTLSTAT_PROMETHEUS_TIMELOOP(time, collector); 6074c163a54SAlan Somers printf("# HELP iscsi_%s_dma_time Cumulative DMA time in us\n" 6084c163a54SAlan Somers "# TYPE iscsi_%s_dma_time counter\n", collector, collector); 6094c163a54SAlan Somers CTLSTAT_PROMETHEUS_TIMELOOP(dma_time, collector); 6101a7f22d9SAlan Somers 6111a7f22d9SAlan Somers for (i = 0; i < targdata.ntargets; i++) 6121a7f22d9SAlan Somers free(targdata.targets[i]); 6131a7f22d9SAlan Somers free(targdata.target); 6141a7f22d9SAlan Somers free(targdata.targets); 6151a7f22d9SAlan Somers 6161a7f22d9SAlan Somers fflush(stdout); 6171a7f22d9SAlan Somers } 6181a7f22d9SAlan Somers 619130f4520SKenneth D. Merry static void 620130f4520SKenneth D. Merry ctlstat_standard(struct ctlstat_context *ctx) { 6215e796316SKenneth D. Merry long double etime; 622130f4520SKenneth D. Merry uint64_t delta_jiffies, delta_idle; 623130f4520SKenneth D. Merry long double cpu_percentage; 6245be4479bSAlexander Motin int i, j, n; 625130f4520SKenneth D. Merry 626130f4520SKenneth D. Merry cpu_percentage = 0; 627130f4520SKenneth D. Merry 628130f4520SKenneth D. Merry if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0)) 629130f4520SKenneth D. Merry errx(1, "error returned from getcpu()"); 630130f4520SKenneth D. Merry 6315e796316SKenneth D. Merry etime = ctx->cur_time.tv_sec - ctx->prev_time.tv_sec + 6325e796316SKenneth D. Merry (ctx->prev_time.tv_nsec - ctx->cur_time.tv_nsec) * 1e-9; 633130f4520SKenneth D. Merry 634130f4520SKenneth D. Merry if (F_CPU(ctx)) { 635130f4520SKenneth D. Merry ctx->prev_total_jiffies = ctx->cur_total_jiffies; 636130f4520SKenneth D. Merry ctx->cur_total_jiffies = ctx->cur_cpu.user + 637130f4520SKenneth D. Merry ctx->cur_cpu.nice + ctx->cur_cpu.system + 638130f4520SKenneth D. Merry ctx->cur_cpu.intr + ctx->cur_cpu.idle; 639130f4520SKenneth D. Merry delta_jiffies = ctx->cur_total_jiffies; 640130f4520SKenneth D. Merry if (F_FIRST(ctx) == 0) 641130f4520SKenneth D. Merry delta_jiffies -= ctx->prev_total_jiffies; 642130f4520SKenneth D. Merry ctx->prev_idle = ctx->cur_idle; 643130f4520SKenneth D. Merry ctx->cur_idle = ctx->cur_cpu.idle; 644130f4520SKenneth D. Merry delta_idle = ctx->cur_idle - ctx->prev_idle; 645130f4520SKenneth D. Merry 646130f4520SKenneth D. Merry cpu_percentage = delta_jiffies - delta_idle; 647130f4520SKenneth D. Merry cpu_percentage /= delta_jiffies; 648130f4520SKenneth D. Merry cpu_percentage *= 100; 649130f4520SKenneth D. Merry } 650130f4520SKenneth D. Merry 651130f4520SKenneth D. Merry if (F_HDR(ctx)) { 652130f4520SKenneth D. Merry ctx->header_interval--; 653130f4520SKenneth D. Merry if (ctx->header_interval <= 0) { 654130f4520SKenneth D. Merry if (F_CPU(ctx)) 655130f4520SKenneth D. Merry fprintf(stdout, " CPU"); 65661639a0aSAlexander Motin if (F_TOTALS(ctx)) { 65761639a0aSAlexander Motin fprintf(stdout, "%s Read %s" 65861639a0aSAlexander Motin " Write %s Total\n", 659bb8f9017SAlexander Motin (F_TIMEVAL(ctx) != 0) ? " " : "", 660bb8f9017SAlexander Motin (F_TIMEVAL(ctx) != 0) ? " " : "", 661bb8f9017SAlexander Motin (F_TIMEVAL(ctx) != 0) ? " " : ""); 6625be4479bSAlexander Motin n = 3; 66361639a0aSAlexander Motin } else { 6645e4b2529SBaptiste Daroussin for (i = n = 0; i < min(ctl_stat_bits, 665bb8f9017SAlexander Motin ctx->cur_items); i++) { 666bb8f9017SAlexander Motin int item; 667130f4520SKenneth D. Merry 668130f4520SKenneth D. Merry /* 669130f4520SKenneth D. Merry * Obviously this won't work with 670130f4520SKenneth D. Merry * LUN numbers greater than a signed 671130f4520SKenneth D. Merry * integer. 672130f4520SKenneth D. Merry */ 673bb8f9017SAlexander Motin item = (int)ctx->cur_stats[i].item; 674130f4520SKenneth D. Merry 675bb8f9017SAlexander Motin if (F_MASK(ctx) && 676bb8f9017SAlexander Motin bit_test(ctx->item_mask, item) == 0) 677130f4520SKenneth D. Merry continue; 678d5a6319aSAlexander Motin fprintf(stdout, "%15.6s%d %s", 679bb8f9017SAlexander Motin F_PORTS(ctx) ? "port" : "lun", item, 680bb8f9017SAlexander Motin (F_TIMEVAL(ctx) != 0) ? " " : ""); 6815be4479bSAlexander Motin if (++n >= ctx->numdevs) 6825be4479bSAlexander Motin break; 683130f4520SKenneth D. Merry } 684130f4520SKenneth D. Merry fprintf(stdout, "\n"); 685130f4520SKenneth D. Merry } 68661639a0aSAlexander Motin if (F_CPU(ctx)) 68761639a0aSAlexander Motin fprintf(stdout, " "); 6885be4479bSAlexander Motin for (i = 0; i < n; i++) 68961639a0aSAlexander Motin fprintf(stdout, "%s KB/t %s MB/s", 690bb8f9017SAlexander Motin (F_TIMEVAL(ctx) != 0) ? " ms" : "", 691130f4520SKenneth D. Merry (F_DMA(ctx) == 0) ? "tps" : "dps"); 692130f4520SKenneth D. Merry fprintf(stdout, "\n"); 693130f4520SKenneth D. Merry ctx->header_interval = 20; 694130f4520SKenneth D. Merry } 695130f4520SKenneth D. Merry } 696130f4520SKenneth D. Merry 69761639a0aSAlexander Motin if (F_CPU(ctx)) 69861639a0aSAlexander Motin fprintf(stdout, "%3.0Lf%%", cpu_percentage); 699130f4520SKenneth D. Merry if (F_TOTALS(ctx) != 0) { 700130f4520SKenneth D. Merry long double mbsec[3]; 701130f4520SKenneth D. Merry long double kb_per_transfer[3]; 702130f4520SKenneth D. Merry long double transfers_per_sec[3]; 703130f4520SKenneth D. Merry long double ms_per_transfer[3]; 704130f4520SKenneth D. Merry long double ms_per_dma[3]; 705130f4520SKenneth D. Merry long double dmas_per_sec[3]; 706130f4520SKenneth D. Merry 707130f4520SKenneth D. Merry for (i = 0; i < 3; i++) 708130f4520SKenneth D. Merry ctx->prev_total_stats[i] = ctx->cur_total_stats[i]; 709130f4520SKenneth D. Merry 710130f4520SKenneth D. Merry memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats)); 711130f4520SKenneth D. Merry 712130f4520SKenneth D. Merry /* Use macros to make the next loop more readable. */ 713bb8f9017SAlexander Motin #define ADD_STATS_BYTES(st, i, j) \ 714bb8f9017SAlexander Motin ctx->cur_total_stats[st].bytes[j] += \ 715bb8f9017SAlexander Motin ctx->cur_stats[i].bytes[j] 716bb8f9017SAlexander Motin #define ADD_STATS_OPERATIONS(st, i, j) \ 717bb8f9017SAlexander Motin ctx->cur_total_stats[st].operations[j] += \ 718bb8f9017SAlexander Motin ctx->cur_stats[i].operations[j] 719bb8f9017SAlexander Motin #define ADD_STATS_DMAS(st, i, j) \ 720bb8f9017SAlexander Motin ctx->cur_total_stats[st].dmas[j] += \ 721bb8f9017SAlexander Motin ctx->cur_stats[i].dmas[j] 722bb8f9017SAlexander Motin #define ADD_STATS_TIME(st, i, j) \ 723bb8f9017SAlexander Motin bintime_add(&ctx->cur_total_stats[st].time[j], \ 724bb8f9017SAlexander Motin &ctx->cur_stats[i].time[j]) 725bb8f9017SAlexander Motin #define ADD_STATS_DMA_TIME(st, i, j) \ 726bb8f9017SAlexander Motin bintime_add(&ctx->cur_total_stats[st].dma_time[j], \ 727bb8f9017SAlexander Motin &ctx->cur_stats[i].dma_time[j]) 728130f4520SKenneth D. Merry 729bb8f9017SAlexander Motin for (i = 0; i < ctx->cur_items; i++) { 730bb8f9017SAlexander Motin if (F_MASK(ctx) && bit_test(ctx->item_mask, 731bb8f9017SAlexander Motin (int)ctx->cur_stats[i].item) == 0) 73261639a0aSAlexander Motin continue; 733130f4520SKenneth D. Merry for (j = 0; j < CTL_STATS_NUM_TYPES; j++) { 734bb8f9017SAlexander Motin ADD_STATS_BYTES(2, i, j); 735bb8f9017SAlexander Motin ADD_STATS_OPERATIONS(2, i, j); 736bb8f9017SAlexander Motin ADD_STATS_DMAS(2, i, j); 737bb8f9017SAlexander Motin ADD_STATS_TIME(2, i, j); 738bb8f9017SAlexander Motin ADD_STATS_DMA_TIME(2, i, j); 739130f4520SKenneth D. Merry } 740bb8f9017SAlexander Motin ADD_STATS_BYTES(0, i, CTL_STATS_READ); 741bb8f9017SAlexander Motin ADD_STATS_OPERATIONS(0, i, CTL_STATS_READ); 742bb8f9017SAlexander Motin ADD_STATS_DMAS(0, i, CTL_STATS_READ); 743bb8f9017SAlexander Motin ADD_STATS_TIME(0, i, CTL_STATS_READ); 744bb8f9017SAlexander Motin ADD_STATS_DMA_TIME(0, i, CTL_STATS_READ); 745130f4520SKenneth D. Merry 746bb8f9017SAlexander Motin ADD_STATS_BYTES(1, i, CTL_STATS_WRITE); 747bb8f9017SAlexander Motin ADD_STATS_OPERATIONS(1, i, CTL_STATS_WRITE); 748bb8f9017SAlexander Motin ADD_STATS_DMAS(1, i, CTL_STATS_WRITE); 749bb8f9017SAlexander Motin ADD_STATS_TIME(1, i, CTL_STATS_WRITE); 750bb8f9017SAlexander Motin ADD_STATS_DMA_TIME(1, i, CTL_STATS_WRITE); 751130f4520SKenneth D. Merry } 752130f4520SKenneth D. Merry 753130f4520SKenneth D. Merry for (i = 0; i < 3; i++) { 754bb8f9017SAlexander Motin compute_stats(&ctx->cur_total_stats[i], 755130f4520SKenneth D. Merry F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i], 756130f4520SKenneth D. Merry etime, &mbsec[i], &kb_per_transfer[i], 757130f4520SKenneth D. Merry &transfers_per_sec[i], 758130f4520SKenneth D. Merry &ms_per_transfer[i], &ms_per_dma[i], 759130f4520SKenneth D. Merry &dmas_per_sec[i]); 760130f4520SKenneth D. Merry if (F_DMA(ctx) != 0) 76161639a0aSAlexander Motin fprintf(stdout, " %5.1Lf", 762130f4520SKenneth D. Merry ms_per_dma[i]); 763bb8f9017SAlexander Motin else if (F_TIMEVAL(ctx) != 0) 76461639a0aSAlexander Motin fprintf(stdout, " %5.1Lf", 765130f4520SKenneth D. Merry ms_per_transfer[i]); 76661639a0aSAlexander Motin fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf", 767130f4520SKenneth D. Merry kb_per_transfer[i], 768130f4520SKenneth D. Merry (F_DMA(ctx) == 0) ? transfers_per_sec[i] : 769130f4520SKenneth D. Merry dmas_per_sec[i], mbsec[i]); 770130f4520SKenneth D. Merry } 771130f4520SKenneth D. Merry } else { 7725e4b2529SBaptiste Daroussin for (i = n = 0; i < min(ctl_stat_bits, ctx->cur_items); i++) { 773130f4520SKenneth D. Merry long double mbsec, kb_per_transfer; 774130f4520SKenneth D. Merry long double transfers_per_sec; 775130f4520SKenneth D. Merry long double ms_per_transfer; 776130f4520SKenneth D. Merry long double ms_per_dma; 777130f4520SKenneth D. Merry long double dmas_per_sec; 778130f4520SKenneth D. Merry 779bb8f9017SAlexander Motin if (F_MASK(ctx) && bit_test(ctx->item_mask, 780bb8f9017SAlexander Motin (int)ctx->cur_stats[i].item) == 0) 781130f4520SKenneth D. Merry continue; 782bb8f9017SAlexander Motin for (j = 0; j < ctx->prev_items; j++) { 783bb8f9017SAlexander Motin if (ctx->prev_stats[j].item == 784bb8f9017SAlexander Motin ctx->cur_stats[i].item) 785bb8f9017SAlexander Motin break; 786bb8f9017SAlexander Motin } 787bb8f9017SAlexander Motin if (j >= ctx->prev_items) 788bb8f9017SAlexander Motin j = -1; 789bb8f9017SAlexander Motin compute_stats(&ctx->cur_stats[i], 790bb8f9017SAlexander Motin j >= 0 ? &ctx->prev_stats[j] : NULL, 79161639a0aSAlexander Motin etime, &mbsec, &kb_per_transfer, 792130f4520SKenneth D. Merry &transfers_per_sec, &ms_per_transfer, 793130f4520SKenneth D. Merry &ms_per_dma, &dmas_per_sec); 794130f4520SKenneth D. Merry if (F_DMA(ctx)) 79561639a0aSAlexander Motin fprintf(stdout, " %5.1Lf", 796130f4520SKenneth D. Merry ms_per_dma); 797bb8f9017SAlexander Motin else if (F_TIMEVAL(ctx) != 0) 79861639a0aSAlexander Motin fprintf(stdout, " %5.1Lf", 799130f4520SKenneth D. Merry ms_per_transfer); 80061639a0aSAlexander Motin fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf", 801130f4520SKenneth D. Merry kb_per_transfer, (F_DMA(ctx) == 0) ? 802130f4520SKenneth D. Merry transfers_per_sec : dmas_per_sec, mbsec); 8035be4479bSAlexander Motin if (++n >= ctx->numdevs) 8045be4479bSAlexander Motin break; 805130f4520SKenneth D. Merry } 806130f4520SKenneth D. Merry } 807130f4520SKenneth D. Merry } 808130f4520SKenneth D. Merry 8094c163a54SAlan Somers static void 8104c163a54SAlan Somers get_and_print_stats(int fd, struct ctlstat_context *ctx, bool ports) 8114c163a54SAlan Somers { 8124c163a54SAlan Somers struct ctl_io_stats *tmp_stats; 8134c163a54SAlan Somers int c; 8144c163a54SAlan Somers 8154c163a54SAlan Somers tmp_stats = ctx->prev_stats; 8164c163a54SAlan Somers ctx->prev_stats = ctx->cur_stats; 8174c163a54SAlan Somers ctx->cur_stats = tmp_stats; 8184c163a54SAlan Somers c = ctx->prev_alloc; 8194c163a54SAlan Somers ctx->prev_alloc = ctx->cur_alloc; 8204c163a54SAlan Somers ctx->cur_alloc = c; 8214c163a54SAlan Somers c = ctx->prev_items; 8224c163a54SAlan Somers ctx->prev_items = ctx->cur_items; 8234c163a54SAlan Somers ctx->cur_items = c; 8244c163a54SAlan Somers ctx->prev_time = ctx->cur_time; 8254c163a54SAlan Somers ctx->prev_cpu = ctx->cur_cpu; 8264c163a54SAlan Somers if (getstats(fd, &ctx->cur_alloc, &ctx->cur_items, 8274c163a54SAlan Somers &ctx->cur_stats, &ctx->cur_time, &ctx->flags, ports) != 0) 8284c163a54SAlan Somers errx(1, "error returned from getstats()"); 8294c163a54SAlan Somers 8304c163a54SAlan Somers switch(ctx->mode) { 8314c163a54SAlan Somers case CTLSTAT_MODE_STANDARD: 8324c163a54SAlan Somers ctlstat_standard(ctx); 8334c163a54SAlan Somers break; 8344c163a54SAlan Somers case CTLSTAT_MODE_DUMP: 8354c163a54SAlan Somers ctlstat_dump(ctx); 8364c163a54SAlan Somers break; 8374c163a54SAlan Somers case CTLSTAT_MODE_JSON: 8384c163a54SAlan Somers ctlstat_json(ctx); 8394c163a54SAlan Somers break; 8404c163a54SAlan Somers case CTLSTAT_MODE_PROMETHEUS: 8414c163a54SAlan Somers ctlstat_prometheus(fd, ctx, ports); 8424c163a54SAlan Somers break; 8434c163a54SAlan Somers default: 8444c163a54SAlan Somers break; 8454c163a54SAlan Somers } 8464c163a54SAlan Somers } 8474c163a54SAlan Somers 848130f4520SKenneth D. Merry int 849130f4520SKenneth D. Merry main(int argc, char **argv) 850130f4520SKenneth D. Merry { 851130f4520SKenneth D. Merry int c; 852130f4520SKenneth D. Merry int count, waittime; 853130f4520SKenneth D. Merry int fd, retval; 8545e4b2529SBaptiste Daroussin size_t size; 855130f4520SKenneth D. Merry struct ctlstat_context ctx; 856130f4520SKenneth D. Merry 857130f4520SKenneth D. Merry /* default values */ 858130f4520SKenneth D. Merry retval = 0; 859130f4520SKenneth D. Merry waittime = 1; 860130f4520SKenneth D. Merry count = -1; 861130f4520SKenneth D. Merry memset(&ctx, 0, sizeof(ctx)); 862130f4520SKenneth D. Merry ctx.numdevs = 3; 863130f4520SKenneth D. Merry ctx.mode = CTLSTAT_MODE_STANDARD; 864130f4520SKenneth D. Merry ctx.flags |= CTLSTAT_FLAG_CPU; 865130f4520SKenneth D. Merry ctx.flags |= CTLSTAT_FLAG_FIRST_RUN; 866130f4520SKenneth D. Merry ctx.flags |= CTLSTAT_FLAG_HEADER; 867130f4520SKenneth D. Merry 8685e4b2529SBaptiste Daroussin size = sizeof(ctl_stat_bits); 8695e4b2529SBaptiste Daroussin if (sysctlbyname("kern.cam.ctl.max_luns", &ctl_stat_bits, &size, NULL, 8705e4b2529SBaptiste Daroussin 0) == -1) { 8715e4b2529SBaptiste Daroussin /* Backward compatibility for where the sysctl wasn't exposed */ 8725e4b2529SBaptiste Daroussin ctl_stat_bits = 1024; 8735e4b2529SBaptiste Daroussin } 8745e4b2529SBaptiste Daroussin ctx.item_mask = bit_alloc(ctl_stat_bits); 8755e4b2529SBaptiste Daroussin if (ctx.item_mask == NULL) 8765e4b2529SBaptiste Daroussin err(1, "bit_alloc() failed"); 8775e4b2529SBaptiste Daroussin 878130f4520SKenneth D. Merry while ((c = getopt(argc, argv, ctlstat_opts)) != -1) { 879130f4520SKenneth D. Merry switch (c) { 880130f4520SKenneth D. Merry case 'C': 881130f4520SKenneth D. Merry ctx.flags &= ~CTLSTAT_FLAG_CPU; 882130f4520SKenneth D. Merry break; 883130f4520SKenneth D. Merry case 'c': 884130f4520SKenneth D. Merry count = atoi(optarg); 885130f4520SKenneth D. Merry break; 886130f4520SKenneth D. Merry case 'd': 887130f4520SKenneth D. Merry ctx.flags |= CTLSTAT_FLAG_DMA_TIME; 888130f4520SKenneth D. Merry break; 889130f4520SKenneth D. Merry case 'D': 890130f4520SKenneth D. Merry ctx.mode = CTLSTAT_MODE_DUMP; 891130f4520SKenneth D. Merry waittime = 30; 892130f4520SKenneth D. Merry break; 893130f4520SKenneth D. Merry case 'h': 894130f4520SKenneth D. Merry ctx.flags &= ~CTLSTAT_FLAG_HEADER; 895130f4520SKenneth D. Merry break; 896130f4520SKenneth D. Merry case 'j': 897130f4520SKenneth D. Merry ctx.mode = CTLSTAT_MODE_JSON; 898130f4520SKenneth D. Merry waittime = 30; 899130f4520SKenneth D. Merry break; 900130f4520SKenneth D. Merry case 'l': { 901130f4520SKenneth D. Merry int cur_lun; 902130f4520SKenneth D. Merry 903130f4520SKenneth D. Merry cur_lun = atoi(optarg); 9045e4b2529SBaptiste Daroussin if (cur_lun > ctl_stat_bits) 905130f4520SKenneth D. Merry errx(1, "Invalid LUN number %d", cur_lun); 906130f4520SKenneth D. Merry 907bb8f9017SAlexander Motin if (!F_MASK(&ctx)) 908130f4520SKenneth D. Merry ctx.numdevs = 1; 909130f4520SKenneth D. Merry else 910130f4520SKenneth D. Merry ctx.numdevs++; 911bb8f9017SAlexander Motin bit_set(ctx.item_mask, cur_lun); 912bb8f9017SAlexander Motin ctx.flags |= CTLSTAT_FLAG_MASK; 913bb8f9017SAlexander Motin ctx.flags |= CTLSTAT_FLAG_LUNS; 914130f4520SKenneth D. Merry break; 915130f4520SKenneth D. Merry } 916130f4520SKenneth D. Merry case 'n': 917130f4520SKenneth D. Merry ctx.numdevs = atoi(optarg); 918130f4520SKenneth D. Merry break; 91961639a0aSAlexander Motin case 'p': { 92061639a0aSAlexander Motin int cur_port; 92161639a0aSAlexander Motin 92261639a0aSAlexander Motin cur_port = atoi(optarg); 9235e4b2529SBaptiste Daroussin if (cur_port > ctl_stat_bits) 924bb8f9017SAlexander Motin errx(1, "Invalid port number %d", cur_port); 92561639a0aSAlexander Motin 926bb8f9017SAlexander Motin if (!F_MASK(&ctx)) 927bb8f9017SAlexander Motin ctx.numdevs = 1; 928bb8f9017SAlexander Motin else 929bb8f9017SAlexander Motin ctx.numdevs++; 930bb8f9017SAlexander Motin bit_set(ctx.item_mask, cur_port); 931bb8f9017SAlexander Motin ctx.flags |= CTLSTAT_FLAG_MASK; 932bb8f9017SAlexander Motin ctx.flags |= CTLSTAT_FLAG_PORTS; 93361639a0aSAlexander Motin break; 93461639a0aSAlexander Motin } 9351a7f22d9SAlan Somers case 'P': 9361a7f22d9SAlan Somers ctx.mode = CTLSTAT_MODE_PROMETHEUS; 9371a7f22d9SAlan Somers break; 938130f4520SKenneth D. Merry case 't': 939130f4520SKenneth D. Merry ctx.flags |= CTLSTAT_FLAG_TOTALS; 940130f4520SKenneth D. Merry break; 941130f4520SKenneth D. Merry case 'w': 942130f4520SKenneth D. Merry waittime = atoi(optarg); 943130f4520SKenneth D. Merry break; 944130f4520SKenneth D. Merry default: 945130f4520SKenneth D. Merry retval = 1; 946130f4520SKenneth D. Merry usage(retval); 947130f4520SKenneth D. Merry exit(retval); 948130f4520SKenneth D. Merry break; 949130f4520SKenneth D. Merry } 950130f4520SKenneth D. Merry } 951130f4520SKenneth D. Merry 952bb8f9017SAlexander Motin if (F_LUNS(&ctx) && F_PORTS(&ctx)) 953bb8f9017SAlexander Motin errx(1, "Options -p and -l are exclusive."); 954bb8f9017SAlexander Motin 9551a7f22d9SAlan Somers if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) { 9561a7f22d9SAlan Somers if ((count != -1) || 9571a7f22d9SAlan Somers (waittime != 1) || 9584c163a54SAlan Somers (F_PORTS(&ctx)) || 9591a7f22d9SAlan Somers /* NB: -P could be compatible with -t in the future */ 9601a7f22d9SAlan Somers (ctx.flags & CTLSTAT_FLAG_TOTALS)) 9611a7f22d9SAlan Somers { 9624c163a54SAlan Somers errx(1, "Option -P is exclusive with -p, -c, -w, and -t"); 9631a7f22d9SAlan Somers } 9641a7f22d9SAlan Somers count = 1; 9651a7f22d9SAlan Somers } 9661a7f22d9SAlan Somers 967bb8f9017SAlexander Motin if (!F_LUNS(&ctx) && !F_PORTS(&ctx)) { 968bb8f9017SAlexander Motin if (F_TOTALS(&ctx)) 969bb8f9017SAlexander Motin ctx.flags |= CTLSTAT_FLAG_PORTS; 970bb8f9017SAlexander Motin else 971bb8f9017SAlexander Motin ctx.flags |= CTLSTAT_FLAG_LUNS; 972bb8f9017SAlexander Motin } 973bb8f9017SAlexander Motin 974130f4520SKenneth D. Merry if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1) 975130f4520SKenneth D. Merry err(1, "cannot open %s", CTL_DEFAULT_DEV); 976130f4520SKenneth D. Merry 9774c163a54SAlan Somers if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) { 9784c163a54SAlan Somers /* 9794c163a54SAlan Somers * NB: Some clients will print a warning if we don't set 9804c163a54SAlan Somers * Content-Length, but they still work. And the data still 9814c163a54SAlan Somers * gets into Prometheus. 9824c163a54SAlan Somers */ 9834c163a54SAlan Somers printf("HTTP/1.1 200 OK\r\n" 9844c163a54SAlan Somers "Connection: close\r\n" 9854c163a54SAlan Somers "Content-Type: text/plain; version=0.0.4\r\n" 9864c163a54SAlan Somers "\r\n"); 9874c163a54SAlan Somers } 988130f4520SKenneth D. Merry 9894c163a54SAlan Somers for (;count != 0;) { 9904c163a54SAlan Somers bool ports; 9914c163a54SAlan Somers 9924c163a54SAlan Somers if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) { 9934c163a54SAlan Somers get_and_print_stats(fd, &ctx, false); 9944c163a54SAlan Somers get_and_print_stats(fd, &ctx, true); 9954c163a54SAlan Somers } else { 9964c163a54SAlan Somers ports = ctx.flags & CTLSTAT_FLAG_PORTS; 9974c163a54SAlan Somers get_and_print_stats(fd, &ctx, ports); 998130f4520SKenneth D. Merry } 999130f4520SKenneth D. Merry 1000130f4520SKenneth D. Merry fprintf(stdout, "\n"); 1001fcc87341SAlexander Motin fflush(stdout); 1002130f4520SKenneth D. Merry ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN; 1003130f4520SKenneth D. Merry if (count != 1) 1004130f4520SKenneth D. Merry sleep(waittime); 1005130f4520SKenneth D. Merry if (count > 0) 1006130f4520SKenneth D. Merry count--; 1007130f4520SKenneth D. Merry } 1008130f4520SKenneth D. Merry 1009130f4520SKenneth D. Merry exit (retval); 1010130f4520SKenneth D. Merry } 1011130f4520SKenneth D. Merry 1012130f4520SKenneth D. Merry /* 1013130f4520SKenneth D. Merry * vim: ts=8 1014130f4520SKenneth D. Merry */ 1015