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)
44 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
45 char *sccsid(void) { return (SCCSID); }
47 unsigned long long uptime[2] = {0, 0};
48 unsigned long long uptime0[2] = {0, 0};
49 struct cifs_stats *st_cifs[2];
50 struct io_hdr_stats *st_hdr_cifs;
52 int cifs_nr = 0; /* Nb of CIFS mounted directories found */
53 int cpu_nr = 0; /* Nb of processors on the machine */
54 int flags = 0; /* Flag for common options and system state */
59 struct sigaction alrm_act;
62 ***************************************************************************
63 * Print usage and exit.
66 * @progname Name of sysstat command.
67 ***************************************************************************
69 void usage(char *progname)
71 fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
75 fprintf(stderr, _("Options are:\n"
76 "[ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n"));
78 fprintf(stderr, _("Options are:\n"
79 "[ -h ] [ -k | -m ] [ -t ] [ -V ]\n"));
85 ***************************************************************************
86 * SIGALRM signal handler.
90 ***************************************************************************
92 void alarm_handler(int sig)
98 ***************************************************************************
99 * Find number of CIFS-mounted points that are registered in
100 * /proc/fs/cifs/Stats.
103 * Number of CIFS-mounted points.
104 ***************************************************************************
106 int get_cifs_nr(void)
112 if ((fp = fopen(CIFSSTATS, "r")) == NULL)
113 /* File non-existent */
116 while (fgets(line, sizeof(line), fp) != NULL) {
118 if (!strncmp(line, "Share (unique mount targets): ", 30)) {
119 sscanf(line + 30, "%d", &cifs);
131 ***************************************************************************
132 * Set every cifs_io entry to inactive state (unregistered).
133 ***************************************************************************
135 void set_entries_inactive(void)
138 struct io_hdr_stats *shi = st_hdr_cifs;
140 for (i = 0; i < cifs_nr; i++, shi++) {
146 ***************************************************************************
147 * Free inactive entries (mark them as unused).
148 ***************************************************************************
150 void free_inactive_entries(void)
153 struct io_hdr_stats *shi = st_hdr_cifs;
155 for (i = 0; i < cifs_nr; i++, shi++) {
163 ***************************************************************************
164 * Allocate and init structures, according to system state.
165 ***************************************************************************
167 void io_sys_init(void)
171 /* How many processors on this machine? */
172 cpu_nr = get_cpu_nr(~0, FALSE);
174 /* Get number of CIFS directories in /proc/fs/cifs/Stats */
175 if ((cifs_nr = get_cifs_nr()) > 0) {
176 cifs_nr += NR_CIFS_PREALLOC;
178 if ((st_hdr_cifs = (struct io_hdr_stats *) calloc(cifs_nr, IO_HDR_STATS_SIZE)) == NULL) {
183 /* Allocate structures for number of CIFS directories found */
184 for (i = 0; i < 2; i++) {
186 (struct cifs_stats *) calloc(cifs_nr, CIFS_STATS_SIZE)) == NULL) {
194 ***************************************************************************
195 * Free various structures.
196 ***************************************************************************
198 void io_sys_free(void)
202 /* Free CIFS directories structures */
203 for (i = 0; i < 2; i++) {
211 ***************************************************************************
212 * Save stats for current CIFS filesystem.
215 * @name Name of CIFS filesystem.
216 * @curr Index in array for current sample statistics.
217 * @st_io Structure with CIFS statistics to save.
218 ***************************************************************************
220 void save_stats(char *name, int curr, struct cifs_stats *st_io)
223 struct io_hdr_stats *st_hdr_cifs_i;
224 struct cifs_stats *st_cifs_i;
226 /* Look for CIFS directory in data table */
227 for (i = 0; i < cifs_nr; i++) {
228 st_hdr_cifs_i = st_hdr_cifs + i;
229 if ((st_hdr_cifs_i->used == TRUE) &&
230 (!strcmp(st_hdr_cifs_i->name, name))) {
237 * This is a new filesystem: Look for an unused entry to store it.
239 for (i = 0; i < cifs_nr; i++) {
240 st_hdr_cifs_i = st_hdr_cifs + i;
241 if (!st_hdr_cifs_i->used) {
242 /* Unused entry found... */
243 st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
244 st_hdr_cifs_i->active = TRUE;
245 strcpy(st_hdr_cifs_i->name, name);
246 st_cifs_i = st_cifs[curr] + i;
247 *st_cifs_i = *((struct cifs_stats *) st_io);
253 * It is a new CIFS directory
254 * but there is no free structure to store it.
257 /* All entries are used: The number has to be increased */
258 cifs_nr = cifs_nr + 5;
260 /* Increase the size of st_hdr_ionfs buffer */
261 if ((st_hdr_cifs = (struct io_hdr_stats *)
262 realloc(st_hdr_cifs, cifs_nr * IO_HDR_STATS_SIZE)) == NULL) {
267 /* Set the new entries inactive */
268 for (j = 0; j < 5; j++) {
269 st_hdr_cifs_i = st_hdr_cifs + i + j;
270 st_hdr_cifs_i->used = FALSE;
271 st_hdr_cifs_i->active = FALSE;
274 /* Increase the size of st_hdr_ionfs buffer */
275 for (j = 0; j < 2; j++) {
276 if ((st_cifs[j] = (struct cifs_stats *)
277 realloc(st_cifs[j], cifs_nr * CIFS_STATS_SIZE)) == NULL) {
281 memset(st_cifs[j] + i, 0, 5 * CIFS_STATS_SIZE);
283 /* Now i shows the first unused entry of the new block */
284 st_hdr_cifs_i = st_hdr_cifs + i;
285 st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
286 st_hdr_cifs_i->active = TRUE;
287 strcpy(st_hdr_cifs_i->name, name);
288 st_cifs_i = st_cifs[curr] + i;
292 st_hdr_cifs_i = st_hdr_cifs + i;
293 st_hdr_cifs_i->active = TRUE;
294 st_hdr_cifs_i->used = TRUE;
295 st_cifs_i = st_cifs[curr] + i;
299 * else it was a new CIFS directory
300 * but there was no free structure to store it.
305 ***************************************************************************
306 * Read CIFS-mount directories stats from /proc/fs/cifs/Stats.
309 * @curr Index in array for current sample statistics.
310 ***************************************************************************
312 void read_cifs_stat(int curr)
318 long long unsigned aux_open;
319 long long unsigned all_open = 0;
320 char cifs_name[MAX_NAME_LEN];
321 char name_tmp[MAX_NAME_LEN];
322 struct cifs_stats scifs;
324 /* Every CIFS entry is potentially unregistered */
325 set_entries_inactive();
327 if ((fp = fopen(CIFSSTATS, "r")) == NULL)
330 sprintf(aux, "%%*d) %%%ds",
331 MAX_NAME_LEN < 200 ? MAX_NAME_LEN - 1 : 200);
333 while (fgets(line, sizeof(line), fp) != NULL) {
335 /* Read CIFS directory name */
336 if (isdigit((unsigned char) line[0]) && sscanf(line, aux , name_tmp) == 1) {
338 scifs.fopens = all_open;
339 save_stats(cifs_name, curr, &scifs);
345 strcpy(cifs_name, name_tmp);
348 if (!strncmp(line, "Reads:", 6)) {
349 sscanf(line, "Reads: %llu Bytes: %llu", &scifs.rd_ops, &scifs.rd_bytes);
351 if (!strncmp(line, "Writes:", 7)) {
352 sscanf(line, "Writes: %llu Bytes: %llu", &scifs.wr_ops, &scifs.wr_bytes);
354 if (!strncmp(line, "Opens:", 6)) {
355 sscanf(line, "Opens: %llu Closes:%llu Deletes: %llu",
356 &aux_open, &scifs.fcloses, &scifs.fdeletes);
357 all_open += aux_open;
359 if (!strncmp(line, "Posix Opens:", 12)) {
360 sscanf(line, "Posix Opens: %llu", &aux_open);
361 all_open += aux_open;
367 scifs.fopens = all_open;
368 save_stats(cifs_name, curr, &scifs);
373 /* Free structures corresponding to unregistered filesystems */
374 free_inactive_entries();
378 ***************************************************************************
379 * Display CIFS stats header.
382 * @fctr Conversion factor.
383 ***************************************************************************
385 void write_cifs_stat_header(int *fctr)
387 printf("Filesystem: ");
388 if (DISPLAY_KILOBYTES(flags)) {
389 printf(" rkB/s wkB/s");
392 else if (DISPLAY_MEGABYTES(flags)) {
393 printf(" rMB/s wMB/s");
397 printf(" rB/s wB/s");
400 printf(" rops/s wops/s fo/s fc/s fd/s\n");
404 ***************************************************************************
405 * Write CIFS stats read from /proc/fs/cifs/Stats.
408 * @curr Index in array for current sample statistics.
409 * @itv Interval of time.
410 * @fctr Conversion factor.
411 * @shi Structures describing the CIFS filesystems.
412 * @ioi Current sample statistics.
413 * @ioj Previous sample statistics.
414 ***************************************************************************
416 void write_cifs_stat(int curr, unsigned long long itv, int fctr,
417 struct io_hdr_stats *shi, struct cifs_stats *ioni,
418 struct cifs_stats *ionj)
420 if (DISPLAY_HUMAN_READ(flags)) {
421 printf("%-22s\n%23s", shi->name, "");
424 printf("%-22s ", shi->name);
427 /* rB/s wB/s fo/s fc/s fd/s*/
428 printf("%12.2f %12.2f %9.2f %9.2f %12.2f %12.2f %12.2f \n",
429 S_VALUE(ionj->rd_bytes, ioni->rd_bytes, itv) / fctr,
430 S_VALUE(ionj->wr_bytes, ioni->wr_bytes, itv) / fctr,
431 S_VALUE(ionj->rd_ops, ioni->rd_ops, itv),
432 S_VALUE(ionj->wr_ops, ioni->wr_ops, itv),
433 S_VALUE(ionj->fopens, ioni->fopens, itv),
434 S_VALUE(ionj->fcloses, ioni->fcloses, itv),
435 S_VALUE(ionj->fdeletes, ioni->fdeletes, itv));
439 ***************************************************************************
440 * Print everything now (stats and uptime).
443 * @curr Index in array for current sample statistics.
444 * @rectime Current date and time.
445 ***************************************************************************
447 void write_stats(int curr, struct tm *rectime)
450 unsigned long long itv;
451 struct io_hdr_stats *shi;
452 struct cifs_stats *ioni, *ionj;
455 TEST_STDOUT(STDOUT_FILENO);
457 /* Print time stamp */
458 if (DISPLAY_TIMESTAMP(flags)) {
459 if (DISPLAY_ISO(flags)) {
460 strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
463 strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
465 printf("%s\n", timestamp);
467 if (DISPLAY_DEBUG(flags)) {
468 fprintf(stderr, "%s\n", timestamp);
473 /* Interval is multiplied by the number of processors */
474 itv = get_interval(uptime[!curr], uptime[curr]);
477 /* On SMP machines, reduce itv to one processor (see note above) */
478 itv = get_interval(uptime0[!curr], uptime0[curr]);
483 /* Display CIFS stats header */
484 write_cifs_stat_header(&fctr);
486 for (i = 0; i < cifs_nr; i++, shi++) {
488 ioni = st_cifs[curr] + i;
489 ionj = st_cifs[!curr] + i;
491 if (DISPLAY_DEBUG(flags)) {
493 fprintf(stderr, "name=%s itv=%llu fctr=%d ioni{ rd_bytes=%llu "
494 "wr_bytes=%llu rd_ops=%llu wr_ops=%llu fopens=%llu "
495 "fcloses=%llu fdeletes=%llu}\n",
496 shi->name, itv, fctr,
497 ioni->rd_bytes, ioni->wr_bytes,
498 ioni->rd_ops, ioni->wr_ops,
499 ioni->fopens, ioni->fcloses,
503 write_cifs_stat(curr, itv, fctr, shi, ioni, ionj);
510 ***************************************************************************
511 * Main loop: Read stats from the relevant sources and display them.
514 * @count Number of lines of stats to print.
515 * @rectime Current date and time.
516 ***************************************************************************
518 void rw_io_stat_loop(long int count, struct tm *rectime)
522 /* Don't buffer data if redirected to a pipe */
523 setbuf(stdout, NULL);
528 * Read system uptime (only for SMP machines).
529 * Init uptime0. So if /proc/uptime cannot fill it,
530 * this will be done by /proc/stat.
533 read_uptime(&(uptime0[curr]));
536 /* Read CIFS stats */
537 read_cifs_stat(curr);
540 get_localtime(rectime, 0);
543 write_stats(curr, rectime);
558 ***************************************************************************
559 * Main entry to the cifsiostat program.
560 ***************************************************************************
562 int main(int argc, char **argv)
568 struct utsname header;
572 /* Init National Language Support */
579 /* Process args... */
583 if (!strcmp(argv[opt], "--debuginfo")) {
588 if (!strncmp(argv[opt], "-", 1)) {
589 for (i = 1; *(argv[opt] + i); i++) {
591 switch (*(argv[opt] + i)) {
594 /* Display an easy-to-read CIFS report */
595 flags |= I_D_HUMAN_READ;
599 if (DISPLAY_MEGABYTES(flags)) {
602 /* Display stats in kB/s */
603 flags |= I_D_KILOBYTES;
607 if (DISPLAY_KILOBYTES(flags)) {
610 /* Display stats in MB/s */
611 flags |= I_D_MEGABYTES;
615 /* Display timestamp */
616 flags |= I_D_TIMESTAMP;
620 /* Print version number and exit */
632 interval = atol(argv[opt++]);
641 count = atol(argv[opt++]);
642 if ((count < 1) || !interval) {
656 /* Init structures according to machine architecture */
659 get_localtime(&rectime, 0);
661 /* Get system name, release number and hostname */
663 if (print_gal_header(&rectime, header.sysname, header.release,
664 header.nodename, header.machine, cpu_nr)) {
669 /* Set a handler for SIGALRM */
670 memset(&alrm_act, 0, sizeof(alrm_act));
671 alrm_act.sa_handler = (void *) alarm_handler;
672 sigaction(SIGALRM, &alrm_act, NULL);
676 rw_io_stat_loop(count, &rectime);
678 /* Free structures */