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