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 uptime0[2] = {0, 0};
48 struct cifs_stats *st_cifs[2];
49 struct io_hdr_stats *st_hdr_cifs;
51 int cifs_nr = 0; /* Nb of CIFS mounted directories found */
52 int cpu_nr = 0; /* Nb of processors on the machine */
53 int flags = 0; /* Flag for common options and system state */
58 struct sigaction alrm_act;
61 ***************************************************************************
62 * Print usage and exit.
65 * @progname Name of sysstat command.
66 ***************************************************************************
68 void usage(char *progname)
70 fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
74 fprintf(stderr, _("Options are:\n"
75 "[ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n"));
77 fprintf(stderr, _("Options are:\n"
78 "[ -h ] [ -k | -m ] [ -t ] [ -V ]\n"));
84 ***************************************************************************
85 * SIGALRM signal handler.
89 ***************************************************************************
91 void alarm_handler(int sig)
97 ***************************************************************************
98 * Find number of CIFS-mounted points that are registered in
99 * /proc/fs/cifs/Stats.
102 * Number of CIFS-mounted points.
103 ***************************************************************************
105 int get_cifs_nr(void)
111 if ((fp = fopen(CIFSSTATS, "r")) == NULL)
112 /* File non-existent */
115 while (fgets(line, sizeof(line), fp) != NULL) {
117 if (!strncmp(line, "Share (unique mount targets): ", 30)) {
118 sscanf(line + 30, "%d", &cifs);
130 ***************************************************************************
131 * Set every cifs_io entry to inactive state (unregistered).
132 ***************************************************************************
134 void set_entries_inactive(void)
137 struct io_hdr_stats *shi = st_hdr_cifs;
139 for (i = 0; i < cifs_nr; i++, shi++) {
145 ***************************************************************************
146 * Free inactive entries (mark them as unused).
147 ***************************************************************************
149 void free_inactive_entries(void)
152 struct io_hdr_stats *shi = st_hdr_cifs;
154 for (i = 0; i < cifs_nr; i++, shi++) {
162 ***************************************************************************
163 * Allocate and init structures, according to system state.
164 ***************************************************************************
166 void io_sys_init(void)
170 /* How many processors on this machine? */
171 cpu_nr = get_cpu_nr(~0, FALSE);
173 /* Get number of CIFS directories in /proc/fs/cifs/Stats */
174 if ((cifs_nr = get_cifs_nr()) > 0) {
175 cifs_nr += NR_CIFS_PREALLOC;
179 if ((st_hdr_cifs = (struct io_hdr_stats *) calloc(cifs_nr, IO_HDR_STATS_SIZE)) == NULL) {
184 /* Allocate structures for number of CIFS directories found */
185 for (i = 0; i < 2; i++) {
187 (struct cifs_stats *) calloc(cifs_nr, CIFS_STATS_SIZE)) == NULL) {
195 * cifs_nr value is probably zero, but it can also be negative
196 * (possible overflow when adding NR_CIFS_PREALLOC above).
203 ***************************************************************************
204 * Free various structures.
205 ***************************************************************************
207 void io_sys_free(void)
211 /* Free CIFS directories structures */
212 for (i = 0; i < 2; i++) {
220 ***************************************************************************
221 * Save stats for current CIFS filesystem.
224 * @name Name of CIFS filesystem.
225 * @curr Index in array for current sample statistics.
226 * @st_io Structure with CIFS statistics to save.
227 ***************************************************************************
229 void save_stats(char *name, int curr, struct cifs_stats *st_io)
232 struct io_hdr_stats *st_hdr_cifs_i;
233 struct cifs_stats *st_cifs_i;
235 /* Look for CIFS directory in data table */
236 for (i = 0; i < cifs_nr; i++) {
237 st_hdr_cifs_i = st_hdr_cifs + i;
238 if ((st_hdr_cifs_i->used == TRUE) &&
239 (!strcmp(st_hdr_cifs_i->name, name))) {
246 * This is a new filesystem: Look for an unused entry to store it.
248 for (i = 0; i < cifs_nr; i++) {
249 st_hdr_cifs_i = st_hdr_cifs + i;
250 if (!st_hdr_cifs_i->used) {
251 /* Unused entry found... */
252 st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
253 st_hdr_cifs_i->active = TRUE;
254 strncpy(st_hdr_cifs_i->name, name, MAX_NAME_LEN - 1);
255 st_hdr_cifs_i->name[MAX_NAME_LEN - 1] = '\0';
256 st_cifs_i = st_cifs[curr] + i;
257 *st_cifs_i = *((struct cifs_stats *) st_io);
263 * It is a new CIFS directory
264 * but there is no free structure to store it.
267 /* All entries are used: The number has to be increased */
268 cifs_nr = cifs_nr + 5;
270 /* Increase the size of st_hdr_ionfs buffer */
271 if ((st_hdr_cifs = (struct io_hdr_stats *)
272 realloc(st_hdr_cifs, cifs_nr * IO_HDR_STATS_SIZE)) == NULL) {
277 /* Set the new entries inactive */
278 for (j = 0; j < 5; j++) {
279 st_hdr_cifs_i = st_hdr_cifs + i + j;
280 st_hdr_cifs_i->used = FALSE;
281 st_hdr_cifs_i->active = FALSE;
284 /* Increase the size of st_hdr_ionfs buffer */
285 for (j = 0; j < 2; j++) {
286 if ((st_cifs[j] = (struct cifs_stats *)
287 realloc(st_cifs[j], cifs_nr * CIFS_STATS_SIZE)) == NULL) {
291 memset(st_cifs[j] + i, 0, 5 * CIFS_STATS_SIZE);
293 /* Now i shows the first unused entry of the new block */
294 st_hdr_cifs_i = st_hdr_cifs + i;
295 st_hdr_cifs_i->used = TRUE; /* Indicate it is now used */
296 st_hdr_cifs_i->active = TRUE;
297 strncpy(st_hdr_cifs_i->name, name, MAX_NAME_LEN - 1);
298 st_hdr_cifs_i->name[MAX_NAME_LEN - 1] = '\0';
299 st_cifs_i = st_cifs[curr] + i;
303 st_hdr_cifs_i = st_hdr_cifs + i;
304 st_hdr_cifs_i->active = TRUE;
305 st_hdr_cifs_i->used = TRUE;
306 st_cifs_i = st_cifs[curr] + i;
310 * else it was a new CIFS directory
311 * but there was no free structure to store it.
316 ***************************************************************************
317 * Read CIFS-mount directories stats from /proc/fs/cifs/Stats.
320 * @curr Index in array for current sample statistics.
321 ***************************************************************************
323 void read_cifs_stat(int curr)
329 long long unsigned aux_open;
330 long long unsigned all_open = 0;
331 char cifs_name[MAX_NAME_LEN];
332 char name_tmp[MAX_NAME_LEN];
333 struct cifs_stats scifs = {0, 0, 0, 0, 0, 0, 0};
335 /* Every CIFS entry is potentially unregistered */
336 set_entries_inactive();
338 if ((fp = fopen(CIFSSTATS, "r")) == NULL)
341 sprintf(aux, "%%*d) %%%ds",
342 MAX_NAME_LEN < 200 ? MAX_NAME_LEN - 1 : 200);
344 while (fgets(line, sizeof(line), fp) != NULL) {
346 /* Read CIFS directory name */
347 if (isdigit((unsigned char) line[0]) && sscanf(line, aux , name_tmp) == 1) {
349 scifs.fopens = all_open;
350 save_stats(cifs_name, curr, &scifs);
356 strcpy(cifs_name, name_tmp);
359 if (!strncmp(line, "Reads:", 6)) {
360 sscanf(line, "Reads: %llu Bytes: %llu", &scifs.rd_ops, &scifs.rd_bytes);
362 if (!strncmp(line, "Writes:", 7)) {
363 sscanf(line, "Writes: %llu Bytes: %llu", &scifs.wr_ops, &scifs.wr_bytes);
365 if (!strncmp(line, "Opens:", 6)) {
366 sscanf(line, "Opens: %llu Closes:%llu Deletes: %llu",
367 &aux_open, &scifs.fcloses, &scifs.fdeletes);
368 all_open += aux_open;
370 if (!strncmp(line, "Posix Opens:", 12)) {
371 sscanf(line, "Posix Opens: %llu", &aux_open);
372 all_open += aux_open;
378 scifs.fopens = all_open;
379 save_stats(cifs_name, curr, &scifs);
384 /* Free structures corresponding to unregistered filesystems */
385 free_inactive_entries();
389 ***************************************************************************
390 * Display CIFS stats header.
393 * @fctr Conversion factor.
394 ***************************************************************************
396 void write_cifs_stat_header(int *fctr)
398 printf("Filesystem: ");
399 if (DISPLAY_KILOBYTES(flags)) {
400 printf(" rkB/s wkB/s");
403 else if (DISPLAY_MEGABYTES(flags)) {
404 printf(" rMB/s wMB/s");
408 printf(" rB/s wB/s");
411 printf(" rops/s wops/s fo/s fc/s fd/s\n");
415 ***************************************************************************
416 * Write CIFS stats read from /proc/fs/cifs/Stats.
419 * @curr Index in array for current sample statistics.
420 * @itv Interval of time.
421 * @fctr Conversion factor.
422 * @shi Structures describing the CIFS filesystems.
423 * @ioi Current sample statistics.
424 * @ioj Previous sample statistics.
425 ***************************************************************************
427 void write_cifs_stat(int curr, unsigned long long itv, int fctr,
428 struct io_hdr_stats *shi, struct cifs_stats *ioni,
429 struct cifs_stats *ionj)
431 if (DISPLAY_HUMAN_READ(flags)) {
432 printf("%-22s\n%23s", shi->name, "");
435 printf("%-22s ", shi->name);
438 /* rB/s wB/s fo/s fc/s fd/s*/
439 printf("%12.2f %12.2f %9.2f %9.2f %12.2f %12.2f %12.2f \n",
440 S_VALUE(ionj->rd_bytes, ioni->rd_bytes, itv) / fctr,
441 S_VALUE(ionj->wr_bytes, ioni->wr_bytes, itv) / fctr,
442 S_VALUE(ionj->rd_ops, ioni->rd_ops, itv),
443 S_VALUE(ionj->wr_ops, ioni->wr_ops, itv),
444 S_VALUE(ionj->fopens, ioni->fopens, itv),
445 S_VALUE(ionj->fcloses, ioni->fcloses, itv),
446 S_VALUE(ionj->fdeletes, ioni->fdeletes, itv));
450 ***************************************************************************
451 * Print everything now (stats and uptime).
454 * @curr Index in array for current sample statistics.
455 * @rectime Current date and time.
456 ***************************************************************************
458 void write_stats(int curr, struct tm *rectime)
461 unsigned long long itv;
462 struct io_hdr_stats *shi;
463 struct cifs_stats *ioni, *ionj;
466 TEST_STDOUT(STDOUT_FILENO);
468 /* Print time stamp */
469 if (DISPLAY_TIMESTAMP(flags)) {
470 if (DISPLAY_ISO(flags)) {
471 strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
474 strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
476 printf("%s\n", timestamp);
478 if (DISPLAY_DEBUG(flags)) {
479 fprintf(stderr, "%s\n", timestamp);
484 /* Interval of time, reduced to one processor */
485 itv = get_interval(uptime0[!curr], uptime0[curr]);
489 /* Display CIFS stats header */
490 write_cifs_stat_header(&fctr);
492 for (i = 0; i < cifs_nr; i++, shi++) {
494 ioni = st_cifs[curr] + i;
495 ionj = st_cifs[!curr] + i;
497 if (DISPLAY_DEBUG(flags)) {
499 fprintf(stderr, "name=%s itv=%llu fctr=%d ioni{ rd_bytes=%llu "
500 "wr_bytes=%llu rd_ops=%llu wr_ops=%llu fopens=%llu "
501 "fcloses=%llu fdeletes=%llu}\n",
502 shi->name, itv, fctr,
503 ioni->rd_bytes, ioni->wr_bytes,
504 ioni->rd_ops, ioni->wr_ops,
505 ioni->fopens, ioni->fcloses,
509 write_cifs_stat(curr, itv, fctr, shi, ioni, ionj);
516 ***************************************************************************
517 * Main loop: Read stats from the relevant sources and display them.
520 * @count Number of lines of stats to print.
521 * @rectime Current date and time.
522 ***************************************************************************
524 void rw_io_stat_loop(long int count, struct tm *rectime)
528 /* Don't buffer data if redirected to a pipe */
529 setbuf(stdout, NULL);
532 /* Read system uptime (reduced to one processor) */
534 read_uptime(&(uptime0[curr]));
536 /* Cannot read system uptime (/proc/uptime doesn't exist) */
539 /* Read CIFS stats */
540 read_cifs_stat(curr);
543 get_localtime(rectime, 0);
546 write_stats(curr, rectime);
561 ***************************************************************************
562 * Main entry to the cifsiostat program.
563 ***************************************************************************
565 int main(int argc, char **argv)
571 struct utsname header;
575 /* Init National Language Support */
582 /* Process args... */
586 if (!strcmp(argv[opt], "--debuginfo")) {
591 if (!strncmp(argv[opt], "-", 1)) {
592 for (i = 1; *(argv[opt] + i); i++) {
594 switch (*(argv[opt] + i)) {
597 /* Display an easy-to-read CIFS report */
598 flags |= I_D_HUMAN_READ;
602 if (DISPLAY_MEGABYTES(flags)) {
605 /* Display stats in kB/s */
606 flags |= I_D_KILOBYTES;
610 if (DISPLAY_KILOBYTES(flags)) {
613 /* Display stats in MB/s */
614 flags |= I_D_MEGABYTES;
618 /* Display timestamp */
619 flags |= I_D_TIMESTAMP;
623 /* Print version number and exit */
635 interval = atol(argv[opt++]);
644 count = atol(argv[opt++]);
645 if ((count < 1) || !interval) {
659 /* Init structures according to machine architecture */
662 get_localtime(&rectime, 0);
664 /* Get system name, release number and hostname */
666 if (print_gal_header(&rectime, header.sysname, header.release,
667 header.nodename, header.machine, cpu_nr)) {
672 /* Set a handler for SIGALRM */
673 memset(&alrm_act, 0, sizeof(alrm_act));
674 alrm_act.sa_handler = alarm_handler;
675 sigaction(SIGALRM, &alrm_act, NULL);
679 rw_io_stat_loop(count, &rectime);
681 /* Free structures */