1229997Sken/*- 2229997Sken * Copyright (c) 2004, 2008, 2009 Silicon Graphics International Corp. 3229997Sken * All rights reserved. 4229997Sken * 5229997Sken * Redistribution and use in source and binary forms, with or without 6229997Sken * modification, are permitted provided that the following conditions 7229997Sken * are met: 8229997Sken * 1. Redistributions of source code must retain the above copyright 9229997Sken * notice, this list of conditions, and the following disclaimer, 10229997Sken * without modification. 11229997Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12229997Sken * substantially similar to the "NO WARRANTY" disclaimer below 13229997Sken * ("Disclaimer") and any redistribution must be conditioned upon 14229997Sken * including a substantially similar Disclaimer requirement for further 15229997Sken * binary redistribution. 16229997Sken * 17229997Sken * NO WARRANTY 18229997Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19229997Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20229997Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 21229997Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22229997Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23229997Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24229997Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25229997Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26229997Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27229997Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28229997Sken * POSSIBILITY OF SUCH DAMAGES. 29229997Sken * 30229997Sken * $Id: //depot/users/kenm/FreeBSD-test2/usr.bin/ctlstat/ctlstat.c#4 $ 31229997Sken */ 32229997Sken/* 33229997Sken * CAM Target Layer statistics program 34229997Sken * 35229997Sken * Authors: Ken Merry <ken@FreeBSD.org>, Will Andrews <will@FreeBSD.org> 36229997Sken */ 37229997Sken 38229997Sken#include <sys/cdefs.h> 39229997Sken__FBSDID("$FreeBSD$"); 40229997Sken 41229997Sken#include <sys/ioctl.h> 42229997Sken#include <sys/types.h> 43229997Sken#include <sys/param.h> 44229997Sken#include <sys/time.h> 45229997Sken#include <sys/sysctl.h> 46229997Sken#include <sys/resource.h> 47229997Sken#include <sys/queue.h> 48229997Sken#include <sys/callout.h> 49229997Sken#include <stdint.h> 50229997Sken#include <stdio.h> 51229997Sken#include <stdlib.h> 52229997Sken#include <unistd.h> 53229997Sken#include <fcntl.h> 54229997Sken#include <getopt.h> 55229997Sken#include <string.h> 56229997Sken#include <errno.h> 57229997Sken#include <err.h> 58229997Sken#include <ctype.h> 59229997Sken#include <bitstring.h> 60229997Sken#include <cam/scsi/scsi_all.h> 61229997Sken#include <cam/ctl/ctl.h> 62229997Sken#include <cam/ctl/ctl_io.h> 63229997Sken#include <cam/ctl/ctl_scsi_all.h> 64229997Sken#include <cam/ctl/ctl_util.h> 65229997Sken#include <cam/ctl/ctl_frontend_internal.h> 66229997Sken#include <cam/ctl/ctl_backend.h> 67229997Sken#include <cam/ctl/ctl_ioctl.h> 68229997Sken 69229997Sken/* 70229997Sken * The default amount of space we allocate for LUN storage space. We 71229997Sken * dynamically allocate more if needed. 72229997Sken */ 73229997Sken#define CTL_STAT_NUM_LUNS 30 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 */ 80229997Sken#define CTL_STAT_LUN_BITS 1024L 81229997Sken 82229997Skenstatic const char *ctlstat_opts = "Cc:Ddhjl:n: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) 105229997Sken#define CTLSTAT_FLAG_LUN_TIME_VALID (1 << 5) 106229997Sken#define F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU) 107229997Sken#define F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER) 108229997Sken#define F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN) 109229997Sken#define F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS) 110229997Sken#define F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME) 111229997Sken#define F_LUNVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUN_TIME_VALID) 112229997Sken 113229997Skenstruct ctlstat_context { 114229997Sken ctlstat_mode_types mode; 115229997Sken int flags; 116229997Sken struct ctl_lun_io_stats *cur_lun_stats, *prev_lun_stats, 117229997Sken *tmp_lun_stats; 118229997Sken struct ctl_lun_io_stats cur_total_stats[3], prev_total_stats[3]; 119229997Sken struct timespec cur_time, prev_time; 120229997Sken struct ctl_cpu_stats cur_cpu, prev_cpu; 121229997Sken uint64_t cur_total_jiffies, prev_total_jiffies; 122229997Sken uint64_t cur_idle, prev_idle; 123229997Sken bitstr_t bit_decl(lun_mask, CTL_STAT_LUN_BITS); 124229997Sken int num_luns; 125229997Sken int numdevs; 126229997Sken int header_interval; 127229997Sken}; 128229997Sken 129229997Sken#ifndef min 130229997Sken#define min(x,y) (((x) < (y)) ? (x) : (y)) 131229997Sken#endif 132229997Sken 133229997Skenstatic void usage(int error); 134229997Skenstatic int getstats(int fd, int *num_luns, struct ctl_lun_io_stats **xlun_stats, 135229997Sken struct timespec *cur_time, int *lun_time_valid); 136229997Skenstatic int getcpu(struct ctl_cpu_stats *cpu_stats); 137229997Skenstatic void compute_stats(struct ctl_lun_io_stats *cur_stats, 138229997Sken struct ctl_lun_io_stats *prev_stats, 139229997Sken long double etime, long double *mbsec, 140229997Sken long double *kb_per_transfer, 141229997Sken long double *transfers_per_second, 142229997Sken long double *ms_per_transfer, 143229997Sken long double *ms_per_dma, 144229997Sken long double *dmas_per_second); 145229997Sken 146229997Skenstatic void 147229997Skenusage(int error) 148229997Sken{ 149231772Sken fputs(ctlstat_usage, error ? stderr : stdout); 150229997Sken} 151229997Sken 152229997Skenstatic int 153229997Skengetstats(int fd, int *num_luns, struct ctl_lun_io_stats **xlun_stats, 154229997Sken struct timespec *cur_time, int *flags) 155229997Sken{ 156229997Sken struct ctl_lun_io_stats *lun_stats; 157229997Sken struct ctl_stats stats; 158229997Sken int more_space_count; 159229997Sken 160229997Sken more_space_count = 0; 161229997Sken 162229997Sken if (*num_luns == 0) 163229997Sken *num_luns = CTL_STAT_NUM_LUNS; 164229997Sken 165229997Sken lun_stats = *xlun_stats; 166229997Skenretry: 167229997Sken 168229997Sken if (lun_stats == NULL) { 169229997Sken lun_stats = (struct ctl_lun_io_stats *)malloc( 170229997Sken sizeof(*lun_stats) * *num_luns); 171229997Sken } 172229997Sken 173229997Sken memset(&stats, 0, sizeof(stats)); 174229997Sken stats.alloc_len = *num_luns * sizeof(*lun_stats); 175229997Sken memset(lun_stats, 0, stats.alloc_len); 176229997Sken stats.lun_stats = lun_stats; 177229997Sken 178229997Sken if (ioctl(fd, CTL_GETSTATS, &stats) == -1) 179229997Sken err(1, "error returned from CTL_GETSTATS ioctl"); 180229997Sken 181229997Sken switch (stats.status) { 182229997Sken case CTL_SS_OK: 183229997Sken break; 184229997Sken case CTL_SS_ERROR: 185229997Sken err(1, "CTL_SS_ERROR returned from CTL_GETSTATS ioctl"); 186229997Sken break; 187229997Sken case CTL_SS_NEED_MORE_SPACE: 188229997Sken if (more_space_count > 0) { 189229997Sken errx(1, "CTL_GETSTATS returned NEED_MORE_SPACE again"); 190229997Sken } 191229997Sken *num_luns = stats.num_luns; 192229997Sken free(lun_stats); 193229997Sken lun_stats = NULL; 194229997Sken more_space_count++; 195229997Sken goto retry; 196229997Sken break; /* NOTREACHED */ 197229997Sken default: 198229997Sken errx(1, "unknown status %d returned from CTL_GETSTATS ioctl", 199229997Sken stats.status); 200229997Sken break; 201229997Sken } 202229997Sken 203229997Sken *xlun_stats = lun_stats; 204229997Sken *num_luns = stats.num_luns; 205229997Sken cur_time->tv_sec = stats.timestamp.tv_sec; 206229997Sken cur_time->tv_nsec = stats.timestamp.tv_nsec; 207229997Sken if (stats.flags & CTL_STATS_FLAG_TIME_VALID) 208229997Sken *flags |= CTLSTAT_FLAG_LUN_TIME_VALID; 209229997Sken else 210229997Sken *flags &= ~CTLSTAT_FLAG_LUN_TIME_VALID; 211229997Sken 212229997Sken return (0); 213229997Sken} 214229997Sken 215229997Skenstatic int 216229997Skengetcpu(struct ctl_cpu_stats *cpu_stats) 217229997Sken{ 218229997Sken long cp_time[CPUSTATES]; 219229997Sken size_t cplen; 220229997Sken 221229997Sken cplen = sizeof(cp_time); 222229997Sken 223229997Sken if (sysctlbyname("kern.cp_time", &cp_time, &cplen, NULL, 0) == -1) { 224229997Sken warn("sysctlbyname(kern.cp_time...) failed"); 225229997Sken return (1); 226229997Sken } 227229997Sken 228229997Sken cpu_stats->user = cp_time[CP_USER]; 229229997Sken cpu_stats->nice = cp_time[CP_NICE]; 230229997Sken cpu_stats->system = cp_time[CP_SYS]; 231229997Sken cpu_stats->intr = cp_time[CP_INTR]; 232229997Sken cpu_stats->idle = cp_time[CP_IDLE]; 233229997Sken 234229997Sken return (0); 235229997Sken} 236229997Sken 237229997Skenstatic void 238229997Skencompute_stats(struct ctl_lun_io_stats *cur_stats, 239229997Sken struct ctl_lun_io_stats *prev_stats, long double etime, 240229997Sken long double *mbsec, long double *kb_per_transfer, 241229997Sken long double *transfers_per_second, long double *ms_per_transfer, 242229997Sken long double *ms_per_dma, long double *dmas_per_second) 243229997Sken{ 244229997Sken uint64_t total_bytes = 0, total_operations = 0, total_dmas = 0; 245229997Sken uint32_t port; 246229997Sken struct bintime total_time_bt, total_dma_bt; 247229997Sken struct timespec total_time_ts, total_dma_ts; 248229997Sken int i; 249229997Sken 250229997Sken bzero(&total_time_bt, sizeof(total_time_bt)); 251229997Sken bzero(&total_dma_bt, sizeof(total_dma_bt)); 252229997Sken bzero(&total_time_ts, sizeof(total_time_ts)); 253229997Sken bzero(&total_dma_ts, sizeof(total_dma_ts)); 254229997Sken for (port = 0; port < CTL_MAX_PORTS; port++) { 255229997Sken for (i = 0; i < CTL_STATS_NUM_TYPES; i++) { 256229997Sken total_bytes += cur_stats->ports[port].bytes[i]; 257229997Sken total_operations += 258229997Sken cur_stats->ports[port].operations[i]; 259229997Sken total_dmas += cur_stats->ports[port].num_dmas[i]; 260229997Sken bintime_add(&total_time_bt, 261229997Sken &cur_stats->ports[port].time[i]); 262229997Sken bintime_add(&total_dma_bt, 263229997Sken &cur_stats->ports[port].dma_time[i]); 264229997Sken if (prev_stats != NULL) { 265229997Sken total_bytes -= 266229997Sken prev_stats->ports[port].bytes[i]; 267229997Sken total_operations -= 268229997Sken prev_stats->ports[port].operations[i]; 269229997Sken total_dmas -= 270229997Sken prev_stats->ports[port].num_dmas[i]; 271229997Sken bintime_sub(&total_time_bt, 272229997Sken &prev_stats->ports[port].time[i]); 273229997Sken bintime_sub(&total_dma_bt, 274229997Sken &prev_stats->ports[port].dma_time[i]); 275229997Sken } 276229997Sken } 277229997Sken } 278229997Sken 279229997Sken *mbsec = total_bytes; 280229997Sken *mbsec /= 1024 * 1024; 281229997Sken if (etime > 0.0) 282229997Sken *mbsec /= etime; 283229997Sken else 284229997Sken *mbsec = 0; 285229997Sken *kb_per_transfer = total_bytes; 286229997Sken *kb_per_transfer /= 1024; 287229997Sken if (total_operations > 0) 288229997Sken *kb_per_transfer /= total_operations; 289229997Sken else 290229997Sken *kb_per_transfer = 0; 291229997Sken *transfers_per_second = total_operations; 292229997Sken *dmas_per_second = total_dmas; 293229997Sken if (etime > 0.0) { 294229997Sken *transfers_per_second /= etime; 295229997Sken *dmas_per_second /= etime; 296229997Sken } else { 297229997Sken *transfers_per_second = 0; 298229997Sken *dmas_per_second = 0; 299229997Sken } 300229997Sken 301229997Sken bintime2timespec(&total_time_bt, &total_time_ts); 302229997Sken bintime2timespec(&total_dma_bt, &total_dma_ts); 303229997Sken if (total_operations > 0) { 304229997Sken /* 305229997Sken * Convert the timespec to milliseconds. 306229997Sken */ 307229997Sken *ms_per_transfer = total_time_ts.tv_sec * 1000; 308229997Sken *ms_per_transfer += total_time_ts.tv_nsec / 1000000; 309229997Sken *ms_per_transfer /= total_operations; 310229997Sken } else 311229997Sken *ms_per_transfer = 0; 312229997Sken 313229997Sken if (total_dmas > 0) { 314229997Sken /* 315229997Sken * Convert the timespec to milliseconds. 316229997Sken */ 317229997Sken *ms_per_dma = total_dma_ts.tv_sec * 1000; 318229997Sken *ms_per_dma += total_dma_ts.tv_nsec / 1000000; 319229997Sken *ms_per_dma /= total_dmas; 320229997Sken } else 321229997Sken *ms_per_dma = 0; 322229997Sken} 323229997Sken 324229997Sken/* The dump_stats() and json_stats() functions perform essentially the same 325229997Sken * purpose, but dump the statistics in different formats. JSON is more 326229997Sken * conducive to programming, however. 327229997Sken */ 328229997Sken 329229997Sken#define PRINT_BINTIME(prefix, bt) \ 330229997Sken printf("%s %jd s %ju frac\n", prefix, (intmax_t)(bt).sec, \ 331229997Sken (uintmax_t)(bt).frac) 332229997Skenconst char *iotypes[] = {"NO IO", "READ", "WRITE"}; 333229997Sken 334229997Skenstatic void 335229997Skenctlstat_dump(struct ctlstat_context *ctx) { 336229997Sken int iotype, lun, port; 337229997Sken struct ctl_lun_io_stats *stats = ctx->cur_lun_stats; 338229997Sken 339229997Sken for (lun = 0; lun < ctx->num_luns;lun++) { 340229997Sken printf("lun %d\n", lun); 341229997Sken for (port = 0; port < CTL_MAX_PORTS; port++) { 342229997Sken printf(" port %d\n", 343229997Sken stats[lun].ports[port].targ_port); 344229997Sken for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; 345229997Sken iotype++) { 346229997Sken printf(" io type %d (%s)\n", iotype, 347229997Sken iotypes[iotype]); 348229997Sken printf(" bytes %ju\n", (uintmax_t) 349229997Sken stats[lun].ports[port].bytes[iotype]); 350229997Sken printf(" operations %ju\n", (uintmax_t) 351229997Sken stats[lun].ports[port].operations[iotype]); 352229997Sken PRINT_BINTIME(" io time", 353229997Sken stats[lun].ports[port].time[iotype]); 354229997Sken printf(" num dmas %ju\n", (uintmax_t) 355229997Sken stats[lun].ports[port].num_dmas[iotype]); 356229997Sken PRINT_BINTIME(" dma time", 357229997Sken stats[lun].ports[port].dma_time[iotype]); 358229997Sken } 359229997Sken } 360229997Sken } 361229997Sken} 362229997Sken 363229997Sken#define JSON_BINTIME(prefix, bt) \ 364229997Sken printf("\"%s\":{\"sec\":%jd,\"frac\":%ju},", \ 365229997Sken prefix, (intmax_t)(bt).sec, (uintmax_t)(bt).frac) 366229997Sken 367229997Skenstatic void 368229997Skenctlstat_json(struct ctlstat_context *ctx) { 369229997Sken int iotype, lun, port; 370229997Sken struct ctl_lun_io_stats *stats = ctx->cur_lun_stats; 371229997Sken 372229997Sken printf("{\"luns\":["); 373229997Sken for (lun = 0; lun < ctx->num_luns; lun++) { 374229997Sken printf("{\"ports\":["); 375229997Sken for (port = 0; port < CTL_MAX_PORTS;port++) { 376229997Sken printf("{\"num\":%d,\"io\":[", 377229997Sken stats[lun].ports[port].targ_port); 378229997Sken for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; 379229997Sken iotype++) { 380229997Sken printf("{\"type\":\"%s\",", iotypes[iotype]); 381229997Sken printf("\"bytes\":%ju,", (uintmax_t)stats[ 382229997Sken lun].ports[port].bytes[iotype]); 383229997Sken printf("\"operations\":%ju,", (uintmax_t)stats[ 384229997Sken lun].ports[port].operations[iotype]); 385229997Sken JSON_BINTIME("io time", 386229997Sken stats[lun].ports[port].time[iotype]); 387229997Sken JSON_BINTIME("dma time", 388229997Sken stats[lun].ports[port].dma_time[iotype]); 389229997Sken printf("\"num dmas\":%ju}", (uintmax_t) 390229997Sken stats[lun].ports[port].num_dmas[iotype]); 391229997Sken if (iotype < (CTL_STATS_NUM_TYPES - 1)) 392229997Sken printf(","); /* continue io array */ 393229997Sken } 394229997Sken printf("]}"); /* close port */ 395229997Sken if (port < (CTL_MAX_PORTS - 1)) 396229997Sken printf(","); /* continue port array */ 397229997Sken } 398229997Sken printf("]}"); /* close lun */ 399229997Sken if (lun < (ctx->num_luns - 1)) 400229997Sken printf(","); /* continue lun array */ 401229997Sken } 402229997Sken printf("]}"); /* close luns and toplevel */ 403229997Sken} 404229997Sken 405229997Skenstatic void 406229997Skenctlstat_standard(struct ctlstat_context *ctx) { 407249632Sken long double etime; 408229997Sken uint64_t delta_jiffies, delta_idle; 409229997Sken uint32_t port; 410229997Sken long double cpu_percentage; 411229997Sken int i; 412229997Sken int j; 413229997Sken 414229997Sken cpu_percentage = 0; 415229997Sken 416229997Sken if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0)) 417229997Sken errx(1, "error returned from getcpu()"); 418229997Sken 419249632Sken etime = ctx->cur_time.tv_sec - ctx->prev_time.tv_sec + 420249632Sken (ctx->prev_time.tv_nsec - ctx->cur_time.tv_nsec) * 1e-9; 421229997Sken 422229997Sken if (F_CPU(ctx)) { 423229997Sken ctx->prev_total_jiffies = ctx->cur_total_jiffies; 424229997Sken ctx->cur_total_jiffies = ctx->cur_cpu.user + 425229997Sken ctx->cur_cpu.nice + ctx->cur_cpu.system + 426229997Sken ctx->cur_cpu.intr + ctx->cur_cpu.idle; 427229997Sken delta_jiffies = ctx->cur_total_jiffies; 428229997Sken if (F_FIRST(ctx) == 0) 429229997Sken delta_jiffies -= ctx->prev_total_jiffies; 430229997Sken ctx->prev_idle = ctx->cur_idle; 431229997Sken ctx->cur_idle = ctx->cur_cpu.idle; 432229997Sken delta_idle = ctx->cur_idle - ctx->prev_idle; 433229997Sken 434229997Sken cpu_percentage = delta_jiffies - delta_idle; 435229997Sken cpu_percentage /= delta_jiffies; 436229997Sken cpu_percentage *= 100; 437229997Sken } 438229997Sken 439229997Sken if (F_HDR(ctx)) { 440229997Sken ctx->header_interval--; 441229997Sken if (ctx->header_interval <= 0) { 442229997Sken int hdr_devs; 443229997Sken 444229997Sken hdr_devs = 0; 445229997Sken 446229997Sken if (F_TOTALS(ctx)) { 447229997Sken fprintf(stdout, "%s System Read %s" 448229997Sken "System Write %sSystem Total%s\n", 449229997Sken (F_LUNVAL(ctx) != 0) ? " " : "", 450229997Sken (F_LUNVAL(ctx) != 0) ? " " : "", 451229997Sken (F_LUNVAL(ctx) != 0) ? " " : "", 452229997Sken (F_CPU(ctx) == 0) ? " CPU" : ""); 453229997Sken hdr_devs = 3; 454229997Sken } else { 455229997Sken if (F_CPU(ctx)) 456229997Sken fprintf(stdout, " CPU "); 457229997Sken for (i = 0; i < min(CTL_STAT_LUN_BITS, 458229997Sken ctx->num_luns); i++) { 459229997Sken int lun; 460229997Sken 461229997Sken /* 462229997Sken * Obviously this won't work with 463229997Sken * LUN numbers greater than a signed 464229997Sken * integer. 465229997Sken */ 466229997Sken lun = (int)ctx->cur_lun_stats[i 467229997Sken ].lun_number; 468229997Sken 469229997Sken if (bit_test(ctx->lun_mask, lun) == 0) 470229997Sken continue; 471229997Sken fprintf(stdout, "%15.6s%d ", 472229997Sken "lun", lun); 473229997Sken hdr_devs++; 474229997Sken } 475229997Sken fprintf(stdout, "\n"); 476229997Sken } 477229997Sken for (i = 0; i < hdr_devs; i++) 478229997Sken fprintf(stdout, "%s %sKB/t %s MB/s ", 479229997Sken ((F_CPU(ctx) != 0) && (i == 0) && 480229997Sken (F_TOTALS(ctx) == 0)) ? " " : "", 481229997Sken (F_LUNVAL(ctx) != 0) ? " ms " : "", 482229997Sken (F_DMA(ctx) == 0) ? "tps" : "dps"); 483229997Sken fprintf(stdout, "\n"); 484229997Sken ctx->header_interval = 20; 485229997Sken } 486229997Sken } 487229997Sken 488229997Sken if (F_TOTALS(ctx) != 0) { 489229997Sken long double mbsec[3]; 490229997Sken long double kb_per_transfer[3]; 491229997Sken long double transfers_per_sec[3]; 492229997Sken long double ms_per_transfer[3]; 493229997Sken long double ms_per_dma[3]; 494229997Sken long double dmas_per_sec[3]; 495229997Sken 496229997Sken for (i = 0; i < 3; i++) 497229997Sken ctx->prev_total_stats[i] = ctx->cur_total_stats[i]; 498229997Sken 499229997Sken memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats)); 500229997Sken 501229997Sken /* Use macros to make the next loop more readable. */ 502229997Sken#define ADD_STATS_BYTES(st, p, i, j) \ 503229997Sken ctx->cur_total_stats[st].ports[p].bytes[j] += \ 504229997Sken ctx->cur_lun_stats[i].ports[p].bytes[j] 505229997Sken#define ADD_STATS_OPERATIONS(st, p, i, j) \ 506229997Sken ctx->cur_total_stats[st].ports[p].operations[j] += \ 507229997Sken ctx->cur_lun_stats[i].ports[p].operations[j] 508229997Sken#define ADD_STATS_NUM_DMAS(st, p, i, j) \ 509229997Sken ctx->cur_total_stats[st].ports[p].num_dmas[j] += \ 510229997Sken ctx->cur_lun_stats[i].ports[p].num_dmas[j] 511229997Sken#define ADD_STATS_TIME(st, p, i, j) \ 512229997Sken bintime_add(&ctx->cur_total_stats[st].ports[p].time[j], \ 513229997Sken &ctx->cur_lun_stats[i].ports[p].time[j]) 514229997Sken#define ADD_STATS_DMA_TIME(st, p, i, j) \ 515229997Sken bintime_add(&ctx->cur_total_stats[st].ports[p].dma_time[j], \ 516229997Sken &ctx->cur_lun_stats[i].ports[p].dma_time[j]) 517229997Sken 518229997Sken for (i = 0; i < ctx->num_luns; i++) { 519229997Sken for (port = 0; port < CTL_MAX_PORTS; port++) { 520229997Sken for (j = 0; j < CTL_STATS_NUM_TYPES; j++) { 521229997Sken ADD_STATS_BYTES(2, port, i, j); 522229997Sken ADD_STATS_OPERATIONS(2, port, i, j); 523229997Sken ADD_STATS_NUM_DMAS(2, port, i, j); 524229997Sken ADD_STATS_TIME(2, port, i, j); 525229997Sken ADD_STATS_DMA_TIME(2, port, i, j); 526229997Sken } 527229997Sken ADD_STATS_BYTES(0, port, i, CTL_STATS_READ); 528229997Sken ADD_STATS_OPERATIONS(0, port, i, 529229997Sken CTL_STATS_READ); 530229997Sken ADD_STATS_NUM_DMAS(0, port, i, CTL_STATS_READ); 531229997Sken ADD_STATS_TIME(0, port, i, CTL_STATS_READ); 532229997Sken ADD_STATS_DMA_TIME(0, port, i, CTL_STATS_READ); 533229997Sken 534229997Sken ADD_STATS_BYTES(1, port, i, CTL_STATS_WRITE); 535229997Sken ADD_STATS_OPERATIONS(1, port, i, 536229997Sken CTL_STATS_WRITE); 537229997Sken ADD_STATS_NUM_DMAS(1, port, i, CTL_STATS_WRITE); 538229997Sken ADD_STATS_TIME(1, port, i, CTL_STATS_WRITE); 539229997Sken ADD_STATS_DMA_TIME(1, port, i, CTL_STATS_WRITE); 540229997Sken } 541229997Sken } 542229997Sken 543229997Sken for (i = 0; i < 3; i++) { 544229997Sken compute_stats(&ctx->cur_total_stats[i], 545229997Sken F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i], 546229997Sken etime, &mbsec[i], &kb_per_transfer[i], 547229997Sken &transfers_per_sec[i], 548229997Sken &ms_per_transfer[i], &ms_per_dma[i], 549229997Sken &dmas_per_sec[i]); 550229997Sken if (F_DMA(ctx) != 0) 551229997Sken fprintf(stdout, " %2.2Lf", 552229997Sken ms_per_dma[i]); 553229997Sken else if (F_LUNVAL(ctx) != 0) 554229997Sken fprintf(stdout, " %2.2Lf", 555229997Sken ms_per_transfer[i]); 556229997Sken fprintf(stdout, " %5.2Lf %3.0Lf %5.2Lf ", 557229997Sken kb_per_transfer[i], 558229997Sken (F_DMA(ctx) == 0) ? transfers_per_sec[i] : 559229997Sken dmas_per_sec[i], mbsec[i]); 560229997Sken } 561229997Sken if (F_CPU(ctx)) 562229997Sken fprintf(stdout, " %5.1Lf%%", cpu_percentage); 563229997Sken } else { 564229997Sken if (F_CPU(ctx)) 565229997Sken fprintf(stdout, "%5.1Lf%% ", cpu_percentage); 566229997Sken 567229997Sken for (i = 0; i < min(CTL_STAT_LUN_BITS, ctx->num_luns); i++) { 568229997Sken long double mbsec, kb_per_transfer; 569229997Sken long double transfers_per_sec; 570229997Sken long double ms_per_transfer; 571229997Sken long double ms_per_dma; 572229997Sken long double dmas_per_sec; 573229997Sken 574229997Sken if (bit_test(ctx->lun_mask, 575229997Sken (int)ctx->cur_lun_stats[i].lun_number) == 0) 576229997Sken continue; 577229997Sken compute_stats(&ctx->cur_lun_stats[i], F_FIRST(ctx) ? 578229997Sken NULL : &ctx->prev_lun_stats[i], etime, 579229997Sken &mbsec, &kb_per_transfer, 580229997Sken &transfers_per_sec, &ms_per_transfer, 581229997Sken &ms_per_dma, &dmas_per_sec); 582229997Sken if (F_DMA(ctx)) 583229997Sken fprintf(stdout, " %2.2Lf", 584229997Sken ms_per_dma); 585229997Sken else if (F_LUNVAL(ctx) != 0) 586229997Sken fprintf(stdout, " %2.2Lf", 587229997Sken ms_per_transfer); 588229997Sken fprintf(stdout, " %5.2Lf %3.0Lf %5.2Lf ", 589229997Sken kb_per_transfer, (F_DMA(ctx) == 0) ? 590229997Sken transfers_per_sec : dmas_per_sec, mbsec); 591229997Sken } 592229997Sken } 593229997Sken} 594229997Sken 595229997Skenint 596229997Skenmain(int argc, char **argv) 597229997Sken{ 598229997Sken int c; 599229997Sken int count, waittime; 600229997Sken int set_lun; 601229997Sken int fd, retval; 602229997Sken struct ctlstat_context ctx; 603229997Sken 604229997Sken /* default values */ 605229997Sken retval = 0; 606229997Sken waittime = 1; 607229997Sken count = -1; 608229997Sken memset(&ctx, 0, sizeof(ctx)); 609229997Sken ctx.numdevs = 3; 610229997Sken ctx.mode = CTLSTAT_MODE_STANDARD; 611229997Sken ctx.flags |= CTLSTAT_FLAG_CPU; 612229997Sken ctx.flags |= CTLSTAT_FLAG_FIRST_RUN; 613229997Sken ctx.flags |= CTLSTAT_FLAG_HEADER; 614229997Sken 615229997Sken while ((c = getopt(argc, argv, ctlstat_opts)) != -1) { 616229997Sken switch (c) { 617229997Sken case 'C': 618229997Sken ctx.flags &= ~CTLSTAT_FLAG_CPU; 619229997Sken break; 620229997Sken case 'c': 621229997Sken count = atoi(optarg); 622229997Sken break; 623229997Sken case 'd': 624229997Sken ctx.flags |= CTLSTAT_FLAG_DMA_TIME; 625229997Sken break; 626229997Sken case 'D': 627229997Sken ctx.mode = CTLSTAT_MODE_DUMP; 628229997Sken waittime = 30; 629229997Sken break; 630229997Sken case 'h': 631229997Sken ctx.flags &= ~CTLSTAT_FLAG_HEADER; 632229997Sken break; 633229997Sken case 'j': 634229997Sken ctx.mode = CTLSTAT_MODE_JSON; 635229997Sken waittime = 30; 636229997Sken break; 637229997Sken case 'l': { 638229997Sken int cur_lun; 639229997Sken 640229997Sken cur_lun = atoi(optarg); 641229997Sken if (cur_lun > CTL_STAT_LUN_BITS) 642229997Sken errx(1, "Invalid LUN number %d", cur_lun); 643229997Sken 644229997Sken bit_ffs(ctx.lun_mask, CTL_STAT_LUN_BITS, &set_lun); 645229997Sken if (set_lun == -1) 646229997Sken ctx.numdevs = 1; 647229997Sken else 648229997Sken ctx.numdevs++; 649229997Sken bit_set(ctx.lun_mask, cur_lun); 650229997Sken break; 651229997Sken } 652229997Sken case 'n': 653229997Sken ctx.numdevs = atoi(optarg); 654229997Sken break; 655229997Sken case 't': 656229997Sken ctx.flags |= CTLSTAT_FLAG_TOTALS; 657229997Sken ctx.numdevs = 3; 658229997Sken break; 659229997Sken case 'w': 660229997Sken waittime = atoi(optarg); 661229997Sken break; 662229997Sken default: 663229997Sken retval = 1; 664229997Sken usage(retval); 665229997Sken exit(retval); 666229997Sken break; 667229997Sken } 668229997Sken } 669229997Sken 670229997Sken bit_ffs(ctx.lun_mask, CTL_STAT_LUN_BITS, &set_lun); 671229997Sken 672229997Sken if ((F_TOTALS(&ctx)) 673229997Sken && (set_lun != -1)) { 674229997Sken errx(1, "Total Mode (-t) is incompatible with individual " 675229997Sken "LUN mode (-l)"); 676229997Sken } else if (set_lun == -1) { 677229997Sken /* 678229997Sken * Note that this just selects the first N LUNs to display, 679229997Sken * but at this point we have no knoweledge of which LUN 680229997Sken * numbers actually exist. So we may select LUNs that 681229997Sken * aren't there. 682229997Sken */ 683229997Sken bit_nset(ctx.lun_mask, 0, min(ctx.numdevs - 1, 684229997Sken CTL_STAT_LUN_BITS - 1)); 685229997Sken } 686229997Sken 687229997Sken if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1) 688229997Sken err(1, "cannot open %s", CTL_DEFAULT_DEV); 689229997Sken 690229997Sken for (;count != 0;) { 691229997Sken ctx.tmp_lun_stats = ctx.prev_lun_stats; 692229997Sken ctx.prev_lun_stats = ctx.cur_lun_stats; 693229997Sken ctx.cur_lun_stats = ctx.tmp_lun_stats; 694229997Sken ctx.prev_time = ctx.cur_time; 695229997Sken ctx.prev_cpu = ctx.cur_cpu; 696229997Sken if (getstats(fd, &ctx.num_luns, &ctx.cur_lun_stats, 697229997Sken &ctx.cur_time, &ctx.flags) != 0) 698229997Sken errx(1, "error returned from getstats()"); 699229997Sken 700229997Sken switch(ctx.mode) { 701229997Sken case CTLSTAT_MODE_STANDARD: 702229997Sken ctlstat_standard(&ctx); 703229997Sken break; 704229997Sken case CTLSTAT_MODE_DUMP: 705229997Sken ctlstat_dump(&ctx); 706229997Sken break; 707229997Sken case CTLSTAT_MODE_JSON: 708229997Sken ctlstat_json(&ctx); 709229997Sken break; 710229997Sken default: 711229997Sken break; 712229997Sken } 713229997Sken 714229997Sken fprintf(stdout, "\n"); 715229997Sken ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN; 716229997Sken if (count != 1) 717229997Sken sleep(waittime); 718229997Sken if (count > 0) 719229997Sken count--; 720229997Sken } 721229997Sken 722229997Sken exit (retval); 723229997Sken} 724229997Sken 725229997Sken/* 726229997Sken * vim: ts=8 727229997Sken */ 728