2 * cifsiostat: Report I/O statistics for CIFS filesystems.
3 * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved
4 * Written by Ivana Varekova <varekova@redhat.com>
6 ***************************************************************************
7 * This program is free software; you can redistribute it and/or modify it *
8 * under the terms of the GNU General Public License as published by the *
9 * Free Software Foundation; either version 2 of the License, or (at your *
10 * option) any later version. *
12 * This program is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY *
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * You should have received a copy of the GNU General Public License along *
18 * with this program; if not, write to the Free Software Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************
28 #include <sys/utsname.h>
32 #include "cifsiostat.h"
39 #define _(string) gettext(string)
41 #define _(string) (string)
45 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
46 char *sccsid(void) { return (SCCSID); }
50 void int_handler(int n) { return; }
53 unsigned long long uptime_cs[2] = {0, 0};
54 struct cifs_stats *st_cifs[2];
55 struct io_hdr_stats *st_hdr_cifs;
57 int cifs_nr = 0; /* Nb of CIFS mounted directories found */
58 int cpu_nr = 0; /* Nb of processors on the machine */
59 int flags = 0; /* Flag for common options and system state */
60 int dplaces_nr = -1; /* Number of decimal places */
63 char timestamp[TIMESTAMP_LEN];
65 struct sigaction alrm_act;
68 ***************************************************************************
69 * Print usage and exit.
72 * @progname Name of sysstat command.
73 ***************************************************************************
75 void usage(char *progname)
77 fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
81 fprintf(stderr, _("Options are:\n"
82 "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n"));
84 fprintf(stderr, _("Options are:\n"
85 "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ -h ] [ -k | -m ] [ -t ] [ -V ]\n"));
91 ***************************************************************************
92 * SIGALRM signal handler.
96 ***************************************************************************
98 void alarm_handler(int sig)
104 ***************************************************************************
105 * Find number of CIFS-mounted points that are registered in
106 * /proc/fs/cifs/Stats.
109 * Number of CIFS-mounted points.
110 ***************************************************************************
112 int get_cifs_nr(void)
118 if ((fp = fopen(CIFSSTATS, "r")) == NULL)
119 /* File non-existent */
122 while (fgets(line, sizeof(line), fp) != NULL) {
124 if (!strncmp(line, "Share (unique mount targets): ", 30)) {
125 sscanf(line + 30, "%d", &cifs);
137 ***************************************************************************
138 * Set every cifs_io entry to inactive state (unregistered).
139 ***************************************************************************
141 void set_entries_inactive(void)
144 struct io_hdr_stats *shi = st_hdr_cifs;
146 for (i = 0; i < cifs_nr; i++, shi++) {
152 ***************************************************************************
153 * Free inactive entries (mark them as unused).
154 ***************************************************************************
156 void free_inactive_entries(void)
159 struct io_hdr_stats *shi = st_hdr_cifs;
161 for (i = 0; i < cifs_nr; i++, shi++) {
169 ***************************************************************************
170 * Allocate and init structures, according to system state.
171 ***************************************************************************
173 void io_sys_init(void)
177 /* How many processors on this machine? */
178 cpu_nr = get_cpu_nr(~0, FALSE);
180 /* Get number of CIFS directories in /proc/fs/cifs/Stats */
181 if ((cifs_nr = get_cifs_nr()) > 0) {
182 cifs_nr += NR_CIFS_PREALLOC;
186 if ((st_hdr_cifs = (struct io_hdr_stats *) calloc(cifs_nr, IO_HDR_STATS_SIZE)) == NULL) {
191 /* Allocate structures for number of CIFS directories found */
192 for (i = 0; i < 2; i++) {
194 (struct cifs_stats *) calloc(cifs_nr, CIFS_STATS_SIZE)) == NULL) {
202 * cifs_nr value is probably zero, but it can also be negative
203 * (possible overflow when adding NR_CIFS_PREALLOC above).
210 ***************************************************************************
211 * Free various structures.
212 ***************************************************************************
214 void io_sys_free(void)
218 /* Free CIFS directories structures */
219 for (i = 0; i < 2; i++) {
227 ***************************************************************************
228 * Save stats for current CIFS filesystem.
231 * @name Name of CIFS filesystem.
232 * @curr Index in array for current sample statistics.
233 * @st_io Structure with CIFS statistics to save.
234 ***************************************************************************
236 void save_stats(char *name, int curr, struct cifs_stats *st_io)
239 struct io_hdr_stats *st_hdr_cifs_i;
240 struct cifs_stats *st_cifs_i;
242 /* Look for CIFS directory in data table */
243 for (i = 0; i < cifs_nr; i++) {
244 st_hdr_cifs_i = st_hdr_cifs + i;
245 if ((st_hdr_cifs_i->used == TRUE) &&
246 (!strcmp(st_hdr_cifs_i->name, name))) {
253 * This is a new filesystem: Look for an unused entry to store it.
255 for (i = 0; i < cifs_nr; i++) {
256 st_hdr_cifs_i = st_hdr_cifs + i;
257 if (!st_hdr_cifs_i->used) {
258 /* Unused entry found... */
259 st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
260 st_hdr_cifs_i->active = TRUE;
261 strncpy(st_hdr_cifs_i->name, name, MAX_NAME_LEN - 1);
262 st_hdr_cifs_i->name[MAX_NAME_LEN - 1] = '\0';
263 st_cifs_i = st_cifs[curr] + i;
264 *st_cifs_i = *((struct cifs_stats *) st_io);
270 * It is a new CIFS directory
271 * but there is no free structure to store it.
274 /* All entries are used: The number has to be increased */
275 cifs_nr = cifs_nr + 5;
277 /* Increase the size of st_hdr_ionfs buffer */
278 if ((st_hdr_cifs = (struct io_hdr_stats *)
279 realloc(st_hdr_cifs, cifs_nr * IO_HDR_STATS_SIZE)) == NULL) {
284 /* Set the new entries inactive */
285 for (j = 0; j < 5; j++) {
286 st_hdr_cifs_i = st_hdr_cifs + i + j;
287 st_hdr_cifs_i->used = FALSE;
288 st_hdr_cifs_i->active = FALSE;
291 /* Increase the size of st_hdr_ionfs buffer */
292 for (j = 0; j < 2; j++) {
293 if ((st_cifs[j] = (struct cifs_stats *)
294 realloc(st_cifs[j], cifs_nr * CIFS_STATS_SIZE)) == NULL) {
298 memset(st_cifs[j] + i, 0, 5 * CIFS_STATS_SIZE);
300 /* Now i shows the first unused entry of the new block */
301 st_hdr_cifs_i = st_hdr_cifs + i;
302 st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
303 st_hdr_cifs_i->active = TRUE;
304 strncpy(st_hdr_cifs_i->name, name, MAX_NAME_LEN - 1);
305 st_hdr_cifs_i->name[MAX_NAME_LEN - 1] = '\0';
306 st_cifs_i = st_cifs[curr] + i;
310 st_hdr_cifs_i = st_hdr_cifs + i;
311 st_hdr_cifs_i->active = TRUE;
312 st_hdr_cifs_i->used = TRUE;
313 st_cifs_i = st_cifs[curr] + i;
317 * else it was a new CIFS directory
318 * but there was no free structure to store it.
323 ***************************************************************************
324 * Read CIFS-mount directories stats from /proc/fs/cifs/Stats.
327 * @curr Index in array for current sample statistics.
328 ***************************************************************************
330 void read_cifs_stat(int curr)
336 long long unsigned aux_open;
337 long long unsigned all_open = 0;
338 char cifs_name[MAX_NAME_LEN];
339 char name_tmp[MAX_NAME_LEN];
340 struct cifs_stats scifs = {0, 0, 0, 0, 0, 0, 0};
342 /* Every CIFS entry is potentially unregistered */
343 set_entries_inactive();
345 if ((fp = fopen(CIFSSTATS, "r")) == NULL)
348 sprintf(aux, "%%*d) %%%ds",
349 MAX_NAME_LEN < 200 ? MAX_NAME_LEN - 1 : 200);
351 while (fgets(line, sizeof(line), fp) != NULL) {
353 /* Read CIFS directory name */
354 if (isdigit((unsigned char) line[0]) && sscanf(line, aux , name_tmp) == 1) {
356 scifs.fopens = all_open;
357 save_stats(cifs_name, curr, &scifs);
363 strncpy(cifs_name, name_tmp, MAX_NAME_LEN);
364 cifs_name[MAX_NAME_LEN - 1] = '\0';
365 memset(&scifs, 0, sizeof(struct cifs_stats));
368 if (!strncmp(line, "Reads:", 6)) {
370 * SMB1 format: Reads: %llu Bytes: %llu
371 * SMB2 format: Reads: %llu sent %llu failed
372 * If this is SMB2 format then only the first variable (rd_ops) will be set.
374 sscanf(line, "Reads: %llu Bytes: %llu", &scifs.rd_ops, &scifs.rd_bytes);
376 else if (!strncmp(line, "Bytes read:", 11)) {
377 sscanf(line, "Bytes read: %llu Bytes written: %llu",
378 &scifs.rd_bytes, &scifs.wr_bytes);
380 else if (!strncmp(line, "Writes:", 7)) {
382 * SMB1 format: Writes: %llu Bytes: %llu
383 * SMB2 format: Writes: %llu sent %llu failed
384 * If this is SMB2 format then only the first variable (wr_ops) will be set.
386 sscanf(line, "Writes: %llu Bytes: %llu", &scifs.wr_ops, &scifs.wr_bytes);
388 else if (!strncmp(line, "Opens:", 6)) {
389 sscanf(line, "Opens: %llu Closes:%llu Deletes: %llu",
390 &aux_open, &scifs.fcloses, &scifs.fdeletes);
391 all_open += aux_open;
393 else if (!strncmp(line, "Posix Opens:", 12)) {
394 sscanf(line, "Posix Opens: %llu", &aux_open);
395 all_open += aux_open;
397 else if (!strncmp(line, "Open files:", 11)) {
398 sscanf(line, "Open files: %llu total (local), %llu",
399 &all_open, &aux_open);
400 all_open += aux_open;
402 else if (!strncmp(line, "Closes:", 7)) {
403 sscanf(line, "Closes: %llu", &scifs.fcloses);
409 scifs.fopens = all_open;
410 save_stats(cifs_name, curr, &scifs);
415 /* Free structures corresponding to unregistered filesystems */
416 free_inactive_entries();
420 ***************************************************************************
421 * Display CIFS stats header.
424 * @fctr Conversion factor.
425 ***************************************************************************
427 void write_cifs_stat_header(int *fctr)
429 if (!DISPLAY_HUMAN_READ(flags)) {
430 printf("Filesystem ");
432 if (DISPLAY_KILOBYTES(flags)) {
433 printf(" rkB/s wkB/s");
436 else if (DISPLAY_MEGABYTES(flags)) {
437 printf(" rMB/s wMB/s");
441 printf(" rB/s wB/s");
444 printf(" rops/s wops/s fo/s fc/s fd/s");
445 if (DISPLAY_HUMAN_READ(flags)) {
446 printf(" Filesystem");
452 ***************************************************************************
453 * Write CIFS stats read from /proc/fs/cifs/Stats.
456 * @curr Index in array for current sample statistics.
457 * @itv Interval of time (in 1/100th of a second).
458 * @fctr Conversion factor.
459 * @shi Structures describing the CIFS filesystems.
460 * @ioi Current sample statistics.
461 * @ioj Previous sample statistics.
462 ***************************************************************************
464 void write_cifs_stat(int curr, unsigned long long itv, int fctr,
465 struct io_hdr_stats *shi, struct cifs_stats *ioni,
466 struct cifs_stats *ionj)
468 double rbytes, wbytes;
470 if (!DISPLAY_HUMAN_READ(flags)) {
471 cprintf_in(IS_STR, "%-22s", shi->name, 0);
474 /* rB/s wB/s fo/s fc/s fd/s*/
475 rbytes = S_VALUE(ionj->rd_bytes, ioni->rd_bytes, itv);
476 wbytes = S_VALUE(ionj->wr_bytes, ioni->wr_bytes, itv);
477 if (!DISPLAY_UNIT(flags)) {
481 cprintf_f(DISPLAY_UNIT(flags) ? UNIT_BYTE : NO_UNIT, 2, 12, 2,
483 cprintf_f(NO_UNIT, 2, 9, 2,
484 S_VALUE(ionj->rd_ops, ioni->rd_ops, itv),
485 S_VALUE(ionj->wr_ops, ioni->wr_ops, itv));
486 cprintf_f(NO_UNIT, 3, 12, 2,
487 S_VALUE(ionj->fopens, ioni->fopens, itv),
488 S_VALUE(ionj->fcloses, ioni->fcloses, itv),
489 S_VALUE(ionj->fdeletes, ioni->fdeletes, itv));
490 if (DISPLAY_HUMAN_READ(flags)) {
491 cprintf_in(IS_STR, " %s", shi->name, 0);
497 ***************************************************************************
498 * Print everything now (stats and uptime).
501 * @curr Index in array for current sample statistics.
502 * @rectime Current date and time.
503 ***************************************************************************
505 void write_stats(int curr, struct tm *rectime)
508 unsigned long long itv;
509 struct io_hdr_stats *shi;
510 struct cifs_stats *ioni, *ionj;
513 TEST_STDOUT(STDOUT_FILENO);
515 /* Print time stamp */
516 if (DISPLAY_TIMESTAMP(flags)) {
517 if (DISPLAY_ISO(flags)) {
518 strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
521 strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
523 printf("%s\n", timestamp);
525 if (DISPLAY_DEBUG(flags)) {
526 fprintf(stderr, "%s\n", timestamp);
531 /* Interval of time, reduced to one processor */
532 itv = get_interval(uptime_cs[!curr], uptime_cs[curr]);
536 /* Display CIFS stats header */
537 write_cifs_stat_header(&fctr);
539 for (i = 0; i < cifs_nr; i++, shi++) {
541 ioni = st_cifs[curr] + i;
542 ionj = st_cifs[!curr] + i;
544 if (DISPLAY_DEBUG(flags)) {
546 fprintf(stderr, "name=%s itv=%llu fctr=%d ioni{ rd_bytes=%llu "
547 "wr_bytes=%llu rd_ops=%llu wr_ops=%llu fopens=%llu "
548 "fcloses=%llu fdeletes=%llu}\n",
549 shi->name, itv, fctr,
550 ioni->rd_bytes, ioni->wr_bytes,
551 ioni->rd_ops, ioni->wr_ops,
552 ioni->fopens, ioni->fcloses,
556 write_cifs_stat(curr, itv, fctr, shi, ioni, ionj);
563 ***************************************************************************
564 * Main loop: Read stats from the relevant sources and display them.
567 * @count Number of lines of stats to print.
568 * @rectime Current date and time.
569 ***************************************************************************
571 void rw_io_stat_loop(long int count, struct tm *rectime)
575 /* Don't buffer data if redirected to a pipe */
576 setbuf(stdout, NULL);
579 /* Read system uptime in 1/100th of a second */
580 read_uptime(&(uptime_cs[curr]));
582 /* Read CIFS stats */
583 read_cifs_stat(curr);
586 get_localtime(rectime, 0);
589 write_stats(curr, rectime);
604 ***************************************************************************
605 * Main entry to the cifsiostat program.
606 ***************************************************************************
608 int main(int argc, char **argv)
614 struct utsname header;
618 /* Init National Language Support */
622 /* Init color strings */
625 /* Process args... */
629 if (!strcmp(argv[opt], "--debuginfo")) {
635 if (!strcmp(argv[opt], "--human")) {
640 else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
641 /* Get number of decimal places */
642 dplaces_nr = atoi(argv[opt] + 6);
643 if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
649 else if (!strncmp(argv[opt], "-", 1)) {
650 for (i = 1; *(argv[opt] + i); i++) {
652 switch (*(argv[opt] + i)) {
655 /* Display an easy-to-read CIFS report. Also imply --human */
656 flags |= I_D_HUMAN_READ + I_D_UNIT;
660 if (DISPLAY_MEGABYTES(flags)) {
663 /* Display stats in kB/s */
664 flags |= I_D_KILOBYTES;
668 if (DISPLAY_KILOBYTES(flags)) {
671 /* Display stats in MB/s */
672 flags |= I_D_MEGABYTES;
676 /* Display timestamp */
677 flags |= I_D_TIMESTAMP;
681 /* Print version number and exit */
693 interval = atol(argv[opt++]);
702 count = atol(argv[opt++]);
703 if ((count < 1) || !interval) {
717 /* Init structures according to machine architecture */
720 get_localtime(&rectime, 0);
722 /* Get system name, release number and hostname */
724 if (print_gal_header(&rectime, header.sysname, header.release,
725 header.nodename, header.machine, cpu_nr,
731 /* Set a handler for SIGALRM */
732 memset(&alrm_act, 0, sizeof(alrm_act));
733 alrm_act.sa_handler = alarm_handler;
734 sigaction(SIGALRM, &alrm_act, NULL);
738 rw_io_stat_loop(count, &rectime);
740 /* Free structures */