Mercurial > illumos > illumos-gate
changeset 1488:196daa2cf3db
PSARC/2006/034 fsstat
PSARC/2006/116 fsstat update
6335370 RFE: Need generic file system observability (e.g., fsstat)
line wrap: on
line diff
--- a/usr/src/cmd/stat/Makefile Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/cmd/stat/Makefile Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -30,7 +29,7 @@ include ../Makefile.cmd -SUBDIRS= iostat mpstat vmstat +SUBDIRS= iostat mpstat vmstat fsstat all := TARGET = all install := TARGET = install
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/stat/fsstat/Makefile Sat Feb 25 01:33:06 2006 -0800 @@ -0,0 +1,56 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +PROG = fsstat +OBJS = fsstat.o +SRCS =$(OBJS:%.o=%.c) + +include $(SRC)/cmd/Makefile.cmd + +LDLIBS += -lkstat +CFLAGS += $(CCVERBOSE) -I${STATCOMMONDIR} -g +FILEMODE= 0555 +GROUP= bin + +lint := LINTFLAGS = -muxs -I$(STATCOMMONDIR) + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +clean: + -$(RM) $(OBJS) + +lint: lint_SRCS + +include $(SRC)/cmd/Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/stat/fsstat/fsstat.c Sat Feb 25 01:33:06 2006 -0800 @@ -0,0 +1,1079 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <kstat.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <time.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <sys/vnode.h> +#include <sys/vfs.h> +#include <sys/statvfs.h> +#include <sys/fstyp.h> +#include <sys/fsid.h> +#include <sys/mnttab.h> +#include <values.h> +#include <poll.h> +#include <ctype.h> +#include <libintl.h> + +#define OPTIONS "PT:afginv" + +/* Time stamp values */ +#define NODATE 0 /* Default: No time stamp */ +#define DDATE 1 /* Standard date format */ +#define UDATE 2 /* Internal representation of Unix time */ + +#define RETRY_DELAY 250 /* Timeout for poll() */ +#define HEADERLINES 22 /* Number of lines between display headers */ + +#define LBUFSZ 64 /* Generic size for local buffer */ + +/* + * The following are used for the nicenum() function + */ +#define KILO_VAL 1024 +#define ONE_INDEX 3 + +#define NENTITY_INIT 1 /* Initial number of entities to allocate */ + +/* + * We need to have a mechanism for an old/previous and new/current vopstat + * structure. We only need two per entity and we can swap between them. + */ +#define VS_SIZE 2 /* Size of vopstat array */ +#define CUR_INDEX (vs_i) +#define PREV_INDEX ((vs_i == 0) ? 1 : 0) /* Opposite of CUR_INDEX */ +#define BUMP_INDEX() vs_i = ((vs_i == 0) ? 1 : 0) + +/* + * An "entity" is anything we're collecting statistics on, it could + * be a mountpoint or an FS-type. + * e_name is the name of the entity (e.g. mount point or FS-type) + * e_ksname is the name of the associated kstat + * e_vs is an array of vopstats. This is used to keep track of "previous" + * and "current" vopstats. + */ +typedef struct entity { + char *e_name; /* name of entity */ + vopstats_t *e_vs; /* Array of vopstats */ + ulong_t e_fsid; /* fsid for ENTYPE_MNTPT only */ + int e_type; /* type of entity */ + char e_ksname[KSTAT_STRLEN]; /* kstat name */ +} entity_t; + +/* Types of entities (e_type) */ +#define ENTYPE_UNKNOWN 0 /* UNKNOWN must be zero since we calloc() */ +#define ENTYPE_FSTYPE 1 +#define ENTYPE_MNTPT 2 + +/* If more sub-one units are added, make sure to adjust ONE_INDEX above */ +static char units[] = "num KMGTPE"; + +static char *cmdname; /* name of this command */ + +static int vs_i = 0; /* Index of current vs[] slot */ + +static void +usage() +{ + (void) fprintf(stderr, + gettext("Usage: %s [-a|f|i|n|v] [-P] [ fstype | fspath ]... " + "[interval [count]]\n"), cmdname); + exit(2); +} + +/* + * Given a 64-bit number and a starting unit (e.g., n - nanoseconds), + * convert the number to a 5-character representation including any + * decimal point and single-character unit. Put that representation + * into the array "buf" (which had better be big enough). + */ +char * +nicenum(uint64_t num, char unit, char *buf) +{ + uint64_t n = num; + int unit_index; + int index; + char u; + + /* If the user passed in a NUL/zero unit, use the blank value for 1 */ + if (unit == '\0') + unit = ' '; + + unit_index = 0; + while (units[unit_index] != unit) { + unit_index++; + if (unit_index > sizeof (units) - 1) { + (void) sprintf(buf, "??"); + return (buf); + } + } + + index = 0; + while (n >= KILO_VAL) { + n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */ + index++; + unit_index++; + } + + if (unit_index >= sizeof (units) - 1) { + (void) sprintf(buf, "??"); + return (buf); + } + + u = units[unit_index]; + + if (unit_index == ONE_INDEX) { + (void) sprintf(buf, "%llu", (u_longlong_t)n); + } else if (n < 10 && (num & (num - 1)) != 0) { + (void) sprintf(buf, "%.2f%c", + (double)num / (1ULL << 10 * index), u); + } else if (n < 100 && (num & (num - 1)) != 0) { + (void) sprintf(buf, "%.1f%c", + (double)num / (1ULL << 10 * index), u); + } else { + (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u); + } + + return (buf); +} + + +#define RAWVAL(ptr, member) ((ptr)->member.value.ui64) +#define DELTA(member) \ + (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0)) +#define OLDPRINTSTAT(isnice, nicestring, niceval, rawstring, rawval) \ + (isnice) ? \ + (void) printf((nicestring), (niceval)) \ + : \ + (void) printf((rawstring), (rawval)) +#define PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf) \ + (isnice) ? \ + (void) printf((nicestring), nicenum(rawval, unit, buf)) \ + : \ + (void) printf((rawstring), (rawval)) + +/* Values for display flag */ +#define DISP_HEADER 0x1 +#define DISP_RAW 0x2 + +/* + * The policy for dealing with multiple flags is dealt with here. + * Currently, if we are displaying raw output, then don't allow + * headers to be printed. + */ +int +dispflag_policy(int printhdr, int dispflag) +{ + /* If we're not displaying raw output, then allow headers to print */ + if ((dispflag & DISP_RAW) == 0) { + if (printhdr) { + dispflag |= DISP_HEADER; + } + } + + return (dispflag); +} + +static void +dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) +{ + int niceflag = ((dispflag & DISP_RAW) == 0); + longlong_t nnewfile; + longlong_t nnamerm; + longlong_t nnamechg; + longlong_t nattrret; + longlong_t nattrchg; + longlong_t nlookup; + longlong_t nreaddir; + longlong_t ndataread; + longlong_t ndatawrite; + longlong_t readthruput; + longlong_t writethruput; + char buf[LBUFSZ]; + + nnewfile = DELTA(ncreate) + DELTA(nmkdir) + DELTA(nsymlink); + nnamerm = DELTA(nremove) + DELTA(nrmdir); + nnamechg = DELTA(nrename) + DELTA(nlink) + DELTA(nsymlink); + nattrret = DELTA(ngetattr) + DELTA(naccess) + + DELTA(ngetsecattr) + DELTA(nfid); + nattrchg = DELTA(nsetattr) + DELTA(nsetsecattr) + DELTA(nspace); + nlookup = DELTA(nlookup); + nreaddir = DELTA(nreaddir); + ndataread = DELTA(nread); + ndatawrite = DELTA(nwrite); + readthruput = DELTA(read_bytes); + writethruput = DELTA(write_bytes); + + if (dispflag & DISP_HEADER) { + (void) printf(gettext( +" new name name attr attr lookup rddir read read write write\n" +" file remov chng get set ops ops ops bytes ops bytes\n")); + } + + PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf); + (void) printf("%s\n", name); +} + +static void +io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) +{ + int niceflag = ((dispflag & DISP_RAW) == 0); + char buf[LBUFSZ]; + + if (dispflag & DISP_HEADER) { + (void) printf(gettext( +" read read write write rddir rddir rwlock rwulock\n" +" ops bytes ops bytes ops bytes ops ops\n")); + } + + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf); + + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf); + + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf); + + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf); + + (void) printf("%s\n", name); +} + +static void +vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) +{ + int niceflag = ((dispflag & DISP_RAW) == 0); + char buf[LBUFSZ]; + + if (dispflag & DISP_HEADER) { + (void) printf( + gettext(" map addmap delmap getpag putpag pagio\n")); + } + + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf); + (void) printf("%s\n", name); +} + +static void +attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) +{ + int niceflag = ((dispflag & DISP_RAW) == 0); + char buf[LBUFSZ]; + + if (dispflag & DISP_HEADER) { + (void) printf(gettext("getattr setattr getsec setsec\n")); + } + + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf); + + (void) printf("%s\n", name); +} + +static void +naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) +{ + int niceflag = ((dispflag & DISP_RAW) == 0); + char buf[LBUFSZ]; + + if (dispflag & DISP_HEADER) { + (void) printf(gettext( + "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n")); + } + + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf); + (void) printf("%s\n", name); +} + + +#define PRINT_VOPSTAT_CMN(niceflag, vop) \ + if (niceflag) \ + (void) printf("%10s ", #vop); \ + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf); + +#define PRINT_VOPSTAT(niceflag, vop) \ + PRINT_VOPSTAT_CMN(niceflag, vop); \ + if (niceflag) \ + (void) printf("\n"); + +#define PRINT_VOPSTAT_IO(niceflag, vop) \ + PRINT_VOPSTAT_CMN(niceflag, vop); \ + PRINTSTAT(niceflag, " %5s\n", "%lld:", \ + DELTA(vop##_bytes), ' ', buf); + +static void +vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) +{ + int niceflag = ((dispflag & DISP_RAW) == 0); + char buf[LBUFSZ]; + + if (niceflag) { + (void) printf("%s\n", name); + (void) printf(gettext(" operation #ops bytes\n")); + } + + PRINT_VOPSTAT(niceflag, open); + PRINT_VOPSTAT(niceflag, close); + PRINT_VOPSTAT_IO(niceflag, read); + PRINT_VOPSTAT_IO(niceflag, write); + PRINT_VOPSTAT(niceflag, ioctl); + PRINT_VOPSTAT(niceflag, setfl); + PRINT_VOPSTAT(niceflag, getattr); + PRINT_VOPSTAT(niceflag, setattr); + PRINT_VOPSTAT(niceflag, access); + PRINT_VOPSTAT(niceflag, lookup); + PRINT_VOPSTAT(niceflag, create); + PRINT_VOPSTAT(niceflag, remove); + PRINT_VOPSTAT(niceflag, link); + PRINT_VOPSTAT(niceflag, rename); + PRINT_VOPSTAT(niceflag, mkdir); + PRINT_VOPSTAT(niceflag, rmdir); + PRINT_VOPSTAT_IO(niceflag, readdir); + PRINT_VOPSTAT(niceflag, symlink); + PRINT_VOPSTAT(niceflag, readlink); + PRINT_VOPSTAT(niceflag, fsync); + PRINT_VOPSTAT(niceflag, inactive); + PRINT_VOPSTAT(niceflag, fid); + PRINT_VOPSTAT(niceflag, rwlock); + PRINT_VOPSTAT(niceflag, rwunlock); + PRINT_VOPSTAT(niceflag, seek); + PRINT_VOPSTAT(niceflag, cmp); + PRINT_VOPSTAT(niceflag, frlock); + PRINT_VOPSTAT(niceflag, space); + PRINT_VOPSTAT(niceflag, realvp); + PRINT_VOPSTAT(niceflag, getpage); + PRINT_VOPSTAT(niceflag, putpage); + PRINT_VOPSTAT(niceflag, map); + PRINT_VOPSTAT(niceflag, addmap); + PRINT_VOPSTAT(niceflag, delmap); + PRINT_VOPSTAT(niceflag, poll); + PRINT_VOPSTAT(niceflag, dump); + PRINT_VOPSTAT(niceflag, pathconf); + PRINT_VOPSTAT(niceflag, pageio); + PRINT_VOPSTAT(niceflag, dumpctl); + PRINT_VOPSTAT(niceflag, dispose); + PRINT_VOPSTAT(niceflag, getsecattr); + PRINT_VOPSTAT(niceflag, setsecattr); + PRINT_VOPSTAT(niceflag, shrlock); + PRINT_VOPSTAT(niceflag, vnevent); + + if (niceflag) { + /* Make it easier on the eyes */ + (void) printf("\n"); + } else { + (void) printf("%s\n", name); + } +} + + +/* + * Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL, + * then pass it back to the caller. + * + * Returns 0 on success, non-zero on failure. + */ +int +get_vopstats(kstat_ctl_t *kc, char *ksname, vopstats_t *vsp, kstat_t **kspp) +{ + kstat_t *ksp; + + if (ksname == NULL || *ksname == 0) + return (1); + + errno = 0; + /* wait for a possibly up-to-date chain */ + while (kstat_chain_update(kc) == -1) { + if (errno == EAGAIN) { + errno = 0; + (void) poll(NULL, 0, RETRY_DELAY); + continue; + } + perror(gettext("kstat_chain_update")); + exit(1); + } + + if ((ksp = kstat_lookup(kc, NULL, -1, ksname)) == NULL) { + return (1); + } + + if (kstat_read(kc, ksp, vsp) == -1) { + return (1); + } + + if (kspp) + *kspp = ksp; + + return (0); +} + +/* + * Given a file system type name, determine if it's part of the + * exception list of file systems that are not to be displayed. + */ +int +is_exception(char *fsname) +{ + char **xlp; /* Pointer into the exception list */ + + static char *exception_list[] = { + "specfs", + "fifofs", + "fd", + "swapfs", + "ctfs", + "objfs", + "nfsdyn", + NULL + }; + + for (xlp = &exception_list[0]; *xlp != NULL; xlp++) { + if (strcmp(fsname, *xlp) == 0) + return (1); + } + + return (0); +} + +/* + * Plain and simple, build an array of names for fstypes + * Returns 0, if it encounters a problem. + */ +int +build_fstype_list(char ***fstypep) +{ + int i; + int nfstype; + char buf[FSTYPSZ + 1]; + + if ((nfstype = sysfs(GETNFSTYP)) < 0) { + perror(gettext("sysfs(GETNFSTYP)")); + return (0); + } + + if ((*fstypep = calloc(nfstype, sizeof (char *))) == NULL) { + perror(gettext("calloc on fstypes")); + return (0); + } + + for (i = 1; i < nfstype; i++) { + if (sysfs(GETFSTYP, i, buf) < 0) { + perror(gettext("sysfs(GETFSTYP)")); + return (0); + } + + if (buf[0] == 0) + continue; + + /* If this is part of the exception list, move on */ + if (is_exception(buf)) + continue; + + if (((*fstypep)[i] = strdup(buf)) == NULL) { + perror(gettext("strdup() of fstype name")); + return (0); + } + } + + return (i); +} + +/* + * After we're done with getopts(), process the rest of the + * operands. We have three cases and this is the priority: + * + * 1) [ operand... ] interval count + * 2) [ operand... ] interval + * 3) [ operand... ] + * + * The trick is that any of the operands might start with a number or even + * be made up exclusively of numbers (and we have to handle negative numbers + * in case a user/script gets out of line). If we find two operands at the + * end of the list then we claim case 1. If we find only one operand at the + * end made up only of number, then we claim case 2. Otherwise, case 3. + * BTW, argc, argv don't change. + */ +int +parse_operands( + int argc, + char **argv, + int optind, + long *interval, + long *count, + entity_t **entityp) /* Array of stat-able entities */ +{ + int nentities = 0; /* Number of entities found */ + int out_of_range; /* Set if 2nd-to-last operand out-of-range */ + + if (argc == optind) + return (nentities); /* None found, returns 0 */ + /* + * We know exactly what the maximum number of entities is going + * to be: argc - optind + */ + if ((*entityp = calloc((argc - optind), sizeof (entity_t))) == NULL) { + perror(gettext("calloc")); + return (-1); + } + + for (/* void */; argc > optind; optind++) { + char *endptr; + + /* If we have more than two operands left to process */ + if ((argc - optind) > 2) { + (*entityp)[nentities++].e_name = strdup(argv[optind]); + continue; + } + + /* If we're here, then we only have one or two operands left */ + errno = 0; + out_of_range = 0; + *interval = strtol(argv[optind], &endptr, 10); + if (*endptr && !isdigit((int)*endptr)) { + /* Operand was not a number */ + (*entityp)[nentities++].e_name = strdup(argv[optind]); + continue; + } else if (errno == ERANGE || *interval <= 0 || + *interval > MAXLONG) { + /* Operand was a number, just out of range */ + out_of_range++; + } + + /* + * The last operand we saw was a number. If it happened to + * be the last operand, then it is the interval... + */ + if ((argc - optind) == 1) { + /* ...but we need to check the range. */ + if (out_of_range) { + (void) fprintf(stderr, gettext( + "interval must be between 1 and " + "%ld (inclusive)\n"), MAXLONG); + return (-1); + } else { + /* + * The value of the interval is valid. Set + * count to something really big so it goes + * virtually forever. + */ + *count = MAXLONG; + break; + } + } + + /* + * At this point, we *might* have the interval, but if the + * next operand isn't a number, then we don't have either + * the interval nor the count. Both must be set to the + * defaults. In that case, both the current and the previous + * operands are stat-able entities. + */ + errno = 0; + *count = strtol(argv[optind + 1], &endptr, 10); + if (*endptr && !isdigit((int)*endptr)) { + /* + * Faked out! The last operand wasn't a number so + * the current and previous operands should be + * stat-able entities. We also need to reset interval. + */ + *interval = 0; + (*entityp)[nentities++].e_name = strdup(argv[optind++]); + (*entityp)[nentities++].e_name = strdup(argv[optind++]); + } else if (out_of_range || errno == ERANGE || *count <= 0) { + (void) fprintf(stderr, gettext( + "Both interval and count must be between 1 " + "and %ld (inclusive)\n"), MAXLONG); + return (-1); + } + break; /* Done! */ + } + return (nentities); +} + +/* + * set_mntpt() looks at the entity's name (e_name) and finds its + * mountpoint. To do this, we need to build a list of mountpoints + * from /etc/mnttab. We only need to do this once and we don't do it + * if we don't need to look at any mountpoints. + * Returns 0 on success, non-zero if it couldn't find a mount-point. + */ +int +set_mntpt(entity_t *ep) +{ + static struct mnt { + struct mnt *m_next; + char *m_mntpt; + ulong_t m_fsid; /* From statvfs(), set only as needed */ + } *mnt_list = NULL; /* Linked list of mount-points */ + struct mnt *mntp; + struct statvfs statvfsbuf; + char *original_name = ep->e_name; + char path[PATH_MAX]; + + if (original_name == NULL) /* Shouldn't happen */ + return (1); + + /* We only set up mnt_list the first time this is called */ + if (mnt_list == NULL) { + FILE *fp; + struct mnttab mnttab; + + if ((fp = fopen(MNTTAB, "r")) == NULL) { + perror(MNTTAB); + return (1); + } + resetmnttab(fp); + /* + * We insert at the front of the list so that when we + * search entries we'll have the last mounted entries + * first in the list so that we can match the longest + * mountpoint. + */ + while (getmntent(fp, &mnttab) == 0) { + if ((mntp = malloc(sizeof (*mntp))) == NULL) { + perror(gettext("Can't create mount list")); + return (1); + } + mntp->m_mntpt = strdup(mnttab.mnt_mountp); + mntp->m_next = mnt_list; + mnt_list = mntp; + } + (void) fclose(fp); + } + + if (realpath(original_name, path) == NULL) { + perror(original_name); + return (1); + } + + /* + * Now that we have the path, walk through the mnt_list and + * look for the first (best) match. + */ + for (mntp = mnt_list; mntp; mntp = mntp->m_next) { + if (strncmp(path, mntp->m_mntpt, strlen(mntp->m_mntpt)) == 0) { + if (mntp->m_fsid == 0) { + if (statvfs(mntp->m_mntpt, &statvfsbuf)) { + /* Can't statvfs so no match */ + continue; + } else { + mntp->m_fsid = statvfsbuf.f_fsid; + } + } + + if (ep->e_fsid != mntp->m_fsid) { + /* No match - Move on */ + continue; + } + + break; + } + } + + if (mntp == NULL) { + (void) fprintf(stderr, gettext( + "Can't find mount point for %s\n"), path); + return (1); + } + + ep->e_name = strdup(mntp->m_mntpt); + free(original_name); + return (0); +} + +/* + * We have an array of entities that are potentially stat-able. Using + * the name (e_name) of the entity, attempt to construct a ksname suitable + * for use by kstat_lookup(3kstat) and fill it into the e_ksname member. + * + * We check the e_name against the list of file system types. If there is + * no match then test to see if the path is valid. If the path is valid, + * then determine the mountpoint. + */ +void +set_ksnames(entity_t *entities, int nentities, char **fstypes, int nfstypes) +{ + int i, j; + struct statvfs statvfsbuf; + + for (i = 0; i < nentities; i++) { + entity_t *ep = &entities[i]; + + /* Check the name against the list of fstypes */ + for (j = 1; j < nfstypes; j++) { + if (fstypes[j] && ep->e_name && + strcmp(ep->e_name, fstypes[j]) == 0) { + /* It's a file system type */ + ep->e_type = ENTYPE_FSTYPE; + (void) snprintf(ep->e_ksname, + KSTAT_STRLEN, "%s%s", + VOPSTATS_STR, ep->e_name); + /* Now allocate the vopstats array */ + ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t)); + if (entities[i].e_vs == NULL) { + perror(gettext("calloc() vopstats")); + exit(1); + } + break; + } + } + if (j < nfstypes) /* Found it! */ + continue; + + /* + * If the entity in the exception list of fstypes, then + * null out the entry so it isn't displayed and move along. + */ + if (is_exception(ep->e_name)) { + ep->e_ksname[0] = 0; + continue; + } + + /* If we didn't find it, see if it's a path */ + if (ep->e_name == NULL || statvfs(ep->e_name, &statvfsbuf)) { + /* Error - Make sure the entry is nulled out */ + ep->e_ksname[0] = 0; + continue; + } + (void) snprintf(ep->e_ksname, KSTAT_STRLEN, "%s%lx", + VOPSTATS_STR, statvfsbuf.f_fsid); + ep->e_fsid = statvfsbuf.f_fsid; + if (set_mntpt(ep)) { + (void) fprintf(stderr, + gettext("Can't determine type of \"%s\"\n"), + ep->e_name ? ep->e_name : gettext("<NULL>")); + } else { + ep->e_type = ENTYPE_MNTPT; + } + + /* Now allocate the vopstats array */ + ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t)); + if (entities[i].e_vs == NULL) { + perror(gettext("Can't calloc vopstats")); + exit(1); + } + } +} + +void +print_time(int type) +{ + time_t t; + + if (time(&t) != -1) { + if (type == UDATE) { + (void) printf("%ld\n", t); + } else if (type == DDATE) { + char *dstr; + + dstr = ctime(&t); + if (dstr) { + (void) printf("%s", dstr); + } + } + } +} + +/* + * The idea is that 'dspfunc' should only be modified from the default + * once since the display options are mutually exclusive. If 'dspfunc' + * only contains the default display function, then all is good and we + * can set it to the new display function. Otherwise, bail. + */ +void +set_dispfunc( + void (**dspfunc)(char *, vopstats_t *, vopstats_t *, int), + void (*newfunc)(char *, vopstats_t *, vopstats_t *, int)) +{ + if (*dspfunc != dflt_display) { + (void) fprintf(stderr, gettext( + "%s: Display options -{a|f|i|n|v} are mutually exclusive\n"), + cmdname); + usage(); + } + *dspfunc = newfunc; +} + +int +main(int argc, char *argv[]) +{ + int c; + int i, j; /* Generic counters */ + int nentities_found; + int linesout; /* Keeps track of lines printed */ + int printhdr = 0; /* Print a header? 0 = no, 1 = yes */ + int nfstypes; /* Number of fstypes */ + int dispflag = 0; /* Flags for display control */ + int timestamp = NODATE; /* Default: no time stamp */ + long count = 0; /* Number of iterations for display */ + long interval = 0; + boolean_t fstypes_only = B_FALSE; /* Display fstypes only */ + char **fstypes; /* Array of names of all fstypes */ + int nentities; /* Number of stat-able entities */ + entity_t *entities; /* Array of stat-able entities */ + kstat_ctl_t *kc; + void (*dfunc)(char *, vopstats_t *, vopstats_t *, int) = dflt_display; + + extern int optind; + + cmdname = argv[0]; + while ((c = getopt(argc, argv, OPTIONS)) != EOF) { + switch (c) { + + default: + usage(); + break; + + case 'P': /* Parsable output */ + dispflag |= DISP_RAW; + break; + + case 'T': /* Timestamp */ + if (optarg) { + if (strcmp(optarg, "u") == 0) { + timestamp = UDATE; + } else if (strcmp(optarg, "d") == 0) { + timestamp = DDATE; + } + } + + /* If it was never set properly... */ + if (timestamp == NODATE) { + (void) fprintf(stderr, gettext( + "%s: -T option requires either 'u' or 'd'\n"), + cmdname); + usage(); + } + break; + + case 'a': + set_dispfunc(&dfunc, attr_display); + break; + + case 'f': + set_dispfunc(&dfunc, vop_display); + break; + + case 'i': + set_dispfunc(&dfunc, io_display); + break; + + case 'n': + set_dispfunc(&dfunc, naming_display); + break; + + case 'v': + set_dispfunc(&dfunc, vm_display); + break; + } + } + + if ((dispflag & DISP_RAW) && (timestamp != NODATE)) { + (void) fprintf(stderr, gettext( + "-P and -T options are mutually exclusive\n")); + usage(); + } + + /* Gather the list of filesystem types */ + if ((nfstypes = build_fstype_list(&fstypes)) == 0) { + (void) fprintf(stderr, + gettext("Can't build list of fstypes\n")); + exit(1); + } + + nentities = parse_operands( + argc, argv, optind, &interval, &count, &entities); + + if (nentities == -1) /* Set of operands didn't parse properly */ + usage(); + + /* + * If we had no operands (except for interval/count) then we + * fill in the entities[] array with all the fstypes. + */ + if (nentities == 0) { + fstypes_only = B_TRUE; + + if ((entities = calloc(nfstypes, sizeof (entity_t))) == NULL) { + (void) fprintf(stderr, + gettext("Can't calloc fstype stats\n")); + exit(1); + } + + for (i = 1; i < nfstypes; i++) { + if (fstypes[i]) { + entities[nentities].e_name = strdup(fstypes[i]); + nentities++; + } + } + } + + set_ksnames(entities, nentities, fstypes, nfstypes); + + if ((kc = kstat_open()) == NULL) { + perror(gettext("kstat_open")); + exit(1); + } + + /* + * The following loop walks through the entities[] list to "prime + * the pump" + */ + for (j = 0, linesout = 0; j < nentities; j++) { + entity_t *ent = &entities[j]; + vopstats_t *vsp = &ent->e_vs[CUR_INDEX]; + kstat_t *ksp = NULL; + + if (get_vopstats(kc, ent->e_ksname, vsp, &ksp) == 0) { + (*dfunc)(ent->e_name, NULL, vsp, + dispflag_policy(linesout == 0, dispflag)); + linesout++; + } else { + /* + * If we can't find it the first time through, then + * get rid of it. + */ + entities[j].e_ksname[0] = 0; + + /* + * If we're only displaying the fstypes (default + * with no other entities requested) then don't + * complain about any file systems that might not + * be loaded. Otherwise, let the user know that + * he chose poorly. + */ + if (fstypes_only == B_FALSE) { + (void) fprintf(stderr, gettext( + "No statistics available for %s\n"), + entities[j].e_name); + } + } + } + + BUMP_INDEX(); /* Swap the previous/current indices */ + for (i = 1; i <= count; i++) { + /* + * No telling how many lines will be printed in any interval. + * There should be a minimum of HEADERLINES between any + * header. If we exceed that, no big deal. + */ + if (linesout > HEADERLINES) { + linesout = 0; + printhdr = 1; + } + (void) poll(NULL, 0, interval*1000); + + if (timestamp) { + print_time(timestamp); + linesout++; + } + + for (j = 0, nentities_found = 0; j < nentities; j++) { + entity_t *ent = &entities[j]; + + /* + * If this entry has been cleared, don't attempt + * to process it. + */ + if (ent->e_ksname[0] == 0) { + continue; + } + + if (get_vopstats(kc, ent->e_ksname, + &ent->e_vs[CUR_INDEX], NULL) == 0) { + (*dfunc)(ent->e_name, &ent->e_vs[PREV_INDEX], + &ent->e_vs[CUR_INDEX], + dispflag_policy(printhdr, dispflag)); + linesout++; + nentities_found++; + } else { + if (ent->e_type == ENTYPE_MNTPT) { + (void) printf(gettext( + "<<mount point no longer " + "available: %s>>\n"), ent->e_name); + } else if (ent->e_type == ENTYPE_FSTYPE) { + (void) printf(gettext( + "<<file system module no longer " + "loaded: %s>>\n"), ent->e_name); + } else { + (void) printf(gettext( + "<<%s no longer available>>\n"), + ent->e_name); + } + /* Disable this so it doesn't print again */ + ent->e_ksname[0] = 0; + } + printhdr = 0; /* Always shut this off */ + } + BUMP_INDEX(); /* Bump the previous/current indices */ + + /* + * If the entities we were observing are no longer there + * (file system modules unloaded, file systems unmounted) + * then we're done. + */ + if (nentities_found == 0) + break; + } + + return (0); +}
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/pkgdefs/SUNWcsu/prototype_com Sat Feb 25 01:33:06 2006 -0800 @@ -127,6 +127,7 @@ f none usr/bin/fmt 555 root bin f none usr/bin/fmtmsg 555 root bin f none usr/bin/fold 555 root bin +f none usr/bin/fsstat 555 root bin f none usr/bin/geniconvtbl 555 root bin f none usr/bin/getconf 555 root bin f none usr/bin/getdev 555 root bin
--- a/usr/src/uts/common/fs/autofs/auto_vfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/autofs/auto_vfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -94,7 +93,7 @@ VFSDEF_VERSION, "autofs", autofs_init, - VSW_HASPROTO|VSW_CANRWRO|VSW_CANREMOUNT, + VSW_HASPROTO|VSW_CANRWRO|VSW_CANREMOUNT|VSW_STATS, &auto_mntopts };
--- a/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -125,7 +125,7 @@ VFSDEF_VERSION, "hsfs", hsfsinit, - VSW_HASPROTO, /* We don't suppport remounting */ + VSW_HASPROTO|VSW_STATS, /* We don't suppport remounting */ &hsfs_proto_opttbl };
--- a/usr/src/uts/common/fs/lofs/lofs_vfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/lofs/lofs_vfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -59,7 +58,7 @@ VFSDEF_VERSION, "lofs", lofsinit, - VSW_HASPROTO, + VSW_HASPROTO|VSW_STATS, &lofs_mntopts };
--- a/usr/src/uts/common/fs/mntfs/mntvfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/mntfs/mntvfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -68,7 +67,7 @@ VFSDEF_VERSION, "mntfs", mntinit, - VSW_HASPROTO, + VSW_HASPROTO|VSW_STATS, &mnt_mntopts };
--- a/usr/src/uts/common/fs/nfs/nfs4_common.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/nfs/nfs4_common.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -53,7 +52,7 @@ VFSDEF_VERSION, "nfs4", nfs4init, - VSW_CANREMOUNT|VSW_NOTZONESAFE, + VSW_CANREMOUNT|VSW_NOTZONESAFE|VSW_STATS, NULL };
--- a/usr/src/uts/common/fs/nfs/nfs_common.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/nfs/nfs_common.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -142,7 +141,7 @@ VFSDEF_VERSION, "nfs", nfsinit, - VSW_CANREMOUNT|VSW_NOTZONESAFE, + VSW_CANREMOUNT|VSW_NOTZONESAFE|VSW_STATS, NULL }; @@ -159,7 +158,7 @@ VFSDEF_VERSION, "nfs3", nfs3init, - VSW_CANREMOUNT|VSW_NOTZONESAFE, + VSW_CANREMOUNT|VSW_NOTZONESAFE|VSW_STATS, NULL };
--- a/usr/src/uts/common/fs/pcfs/pc_vfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/pcfs/pc_vfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -140,7 +139,7 @@ VFSDEF_VERSION, "pcfs", pcfsinit, - VSW_HASPROTO|VSW_CANREMOUNT, + VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS, &pcfs_mntopts };
--- a/usr/src/uts/common/fs/proc/prvfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/proc/prvfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -72,7 +71,7 @@ VFSDEF_VERSION, "proc", prinit, - VSW_HASPROTO, + VSW_HASPROTO|VSW_STATS, &proc_mntopts };
--- a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -79,7 +78,7 @@ VFSDEF_VERSION, "tmpfs", tmpfsinit, - VSW_HASPROTO, + VSW_HASPROTO|VSW_STATS, &tmpfs_proto_opttbl };
--- a/usr/src/uts/common/fs/udfs/udf_vfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/udfs/udf_vfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -122,7 +121,7 @@ VFSDEF_VERSION, "udfs", udfinit, - VSW_HASPROTO|VSW_CANREMOUNT, + VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS, &udfs_mntopts };
--- a/usr/src/uts/common/fs/ufs/ufs_vfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/ufs/ufs_vfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -197,7 +196,7 @@ VFSDEF_VERSION, "ufs", ufsinit, - VSW_HASPROTO|VSW_CANREMOUNT, + VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS, &ufs_mntopts };
--- a/usr/src/uts/common/fs/vfs.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/vfs.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -494,6 +493,10 @@ vfsp->vfs_mntopts.mo_list = NULL; vfsp->vfs_femhead = NULL; vfsp->vfs_zone = NULL; + /* + * Note: Don't initialize vfs_vskap, vfs_fstypevsp since it + * could be a problem for unbundled file systems. + */ vfs_setops((vfsp), (op)); sema_init(&vfsp->vfs_reflock, 1, NULL, SEMA_DEFAULT, NULL); } @@ -657,6 +660,8 @@ struct vnode *rvp = NULL; char *path; size_t plen; + struct vfssw *vswp; + extern void setup_vopstats(vfs_t *); rw_init(&vfssw_lock, NULL, RW_DEFAULT, NULL); rw_init(&vfslist, NULL, RW_DEFAULT, NULL); @@ -705,6 +710,16 @@ */ clboot_mountroot(); + /* Now that we're all done with the root FS, set up its vopstats */ + if ((vswp = vfs_getvfsswbyvfsops(vfs_getops(rootvfs))) != NULL) { + /* Set flag for statistics collection */ + if (vswp->vsw_flag & VSW_STATS) { + rootvfs->vfs_flag |= VFS_STATS; + } + vfs_unrefvfssw(vswp); + } + setup_vopstats(rootvfs); + /* * Mount /devices, /system/contract, /etc/mnttab, /etc/svc/volatile, * /system/object, and /proc. @@ -848,6 +863,7 @@ char *resource = NULL, *mountpt = NULL; refstr_t *oldresource, *oldmntpt; struct pathname pn, rpn; + extern void setup_vopstats(vfs_t *); /* * The v_flag value for the mount point vp is permanently set @@ -1390,16 +1406,27 @@ optlen, NULL); } } + + /* Set flag for statistics collection */ + if (vswp->vsw_flag & VSW_STATS) { + vfsp->vfs_flag |= VFS_STATS; + } + vfs_unlock(vfsp); } mount_completed(); if (splice) vn_vfsunlock(vp); - /* - * Return vfsp to caller. - */ if ((error == 0) && (copyout_error == 0)) { + /* + * If this isn't a remount, set up the vopstats before + * anyone can touch this + */ + if (!remount) + setup_vopstats(vfsp); + + /* Return vfsp to caller. */ *vfspp = vfsp; } errout: @@ -2458,6 +2485,7 @@ { vnode_t *coveredvp; int error; + extern void teardown_vopstats(vfs_t *); /* * Get covered vnode. This will be NULL if the vfs is not linked @@ -2487,6 +2515,7 @@ if (coveredvp != NULL) vn_vfsunlock(coveredvp); } else if (coveredvp != NULL) { + teardown_vopstats(vfsp); /* * vfs_remove() will do a VN_RELE(vfsp->vfs_vnodecovered) * when it frees vfsp so we do a VN_HOLD() so we can @@ -2497,6 +2526,7 @@ vn_vfsunlock(coveredvp); VN_RELE(coveredvp); } else { + teardown_vopstats(vfsp); /* * Release the reference to vfs that is not linked * into the name space. @@ -3648,6 +3678,8 @@ { struct vfssw *vswp; int error; + extern void vopstats_startup(); + extern void setup_vopstats(vfs_t *); static const fs_operation_def_t EIO_vfsops_template[] = { VFSNAME_MOUNT, vfs_EIO, @@ -3702,6 +3734,9 @@ (*vswp->vsw_init)(vswp - vfssw, vswp->vsw_name); RUNLOCK_VFSSW(); } + + vopstats_startup(); + setup_vopstats(&EIO_vfs); } /*
--- a/usr/src/uts/common/fs/vnode.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/vnode.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -76,6 +75,70 @@ int nfs_global_client_only; /* + * Array of vopstats_t for per-FS-type vopstats. This array has the same + * number of entries as and parallel to the vfssw table. (Arguably, it could + * be part of the vfssw table.) Once it's initialized, it's accessed using + * the same fstype index that is used to index into the vfssw table. + */ +vopstats_t **vopstats_fstype; + +/* vopstats initialization template used for fast initialization via bcopy() */ +static vopstats_t *vs_templatep; + +/* Kmem cache handle for vsk_anchor_t allocations */ +kmem_cache_t *vsk_anchor_cache; + +/* + * Root of AVL tree for the kstats associated with vopstats. Lock protects + * updates to vsktat_tree. + */ +avl_tree_t vskstat_tree; +kmutex_t vskstat_tree_lock; + +/* Global variable which enables/disables the vopstats collection */ +int vopstats_enabled = 1; + +/* + * The following is the common set of actions needed to update the + * vopstats structure from a vnode op. Both VOPSTATS_UPDATE() and + * VOPSTATS_UPDATE_IO() do almost the same thing, except for the + * recording of the bytes transferred. Since the code is similar + * but small, it is nearly a duplicate. Consequently any changes + * to one may need to be reflected in the other. + * Rundown of the variables: + * vp - Pointer to the vnode + * counter - Partial name structure member to update in vopstats for counts + * bytecounter - Partial name structure member to update in vopstats for bytes + * bytesval - Value to update in vopstats for bytes + * fstype - Index into vsanchor_fstype[], same as index into vfssw[] + * vsp - Pointer to vopstats structure (either in vfs or vsanchor_fstype[i]) + */ + +#define VOPSTATS_UPDATE(vp, counter) { \ + vfs_t *vfsp = (vp)->v_vfsp; \ + if (vfsp && (vfsp->vfs_flag & VFS_STATS) && (vp)->v_type != VBAD) { \ + vopstats_t *vsp = &vfsp->vfs_vopstats; \ + vsp->counter.value.ui64++; \ + if ((vsp = vfsp->vfs_fstypevsp) != NULL) { \ + vsp->counter.value.ui64++; \ + } \ + } \ +} + +#define VOPSTATS_UPDATE_IO(vp, counter, bytecounter, bytesval) { \ + vfs_t *vfsp = (vp)->v_vfsp; \ + if (vfsp && (vfsp->vfs_flag & VFS_STATS) && (vp)->v_type != VBAD) { \ + vopstats_t *vsp = &vfsp->vfs_vopstats; \ + vsp->counter.value.ui64++; \ + vsp->bytecounter.value.ui64 += bytesval; \ + if ((vsp = vfsp->vfs_fstypevsp) != NULL) { \ + vsp->counter.value.ui64++; \ + vsp->bytecounter.value.ui64 += bytesval; \ + } \ + } \ +} + +/* * Convert stat(2) formats to vnode types and vice versa. (Knows about * numerical order of S_IFMT and vnode types.) */ @@ -241,6 +304,329 @@ NULL, 0, NULL, NULL }; +/* + * Used by the AVL routines to compare two vsk_anchor_t structures in the tree. + * We use the f_fsid reported by VFS_STATVFS() since we use that for the + * kstat name. + */ +static int +vska_compar(const void *n1, const void *n2) +{ + int ret; + ulong_t p1 = ((vsk_anchor_t *)n1)->vsk_fsid; + ulong_t p2 = ((vsk_anchor_t *)n2)->vsk_fsid; + + if (p1 < p2) { + ret = -1; + } else if (p1 > p2) { + ret = 1; + } else { + ret = 0; + } + + return (ret); +} + +/* + * Used to create a single template which will be bcopy()ed to a newly + * allocated vsanchor_combo_t structure in new_vsanchor(), below. + */ +static vopstats_t * +create_vopstats_template() +{ + vopstats_t *vsp; + + vsp = kmem_alloc(sizeof (vopstats_t), KM_SLEEP); + bzero(vsp, sizeof (*vsp)); /* Start fresh */ + + /* VOP_OPEN */ + kstat_named_init(&vsp->nopen, "nopen", KSTAT_DATA_UINT64); + /* VOP_CLOSE */ + kstat_named_init(&vsp->nclose, "nclose", KSTAT_DATA_UINT64); + /* VOP_READ I/O */ + kstat_named_init(&vsp->nread, "nread", KSTAT_DATA_UINT64); + kstat_named_init(&vsp->read_bytes, "read_bytes", KSTAT_DATA_UINT64); + /* VOP_WRITE I/O */ + kstat_named_init(&vsp->nwrite, "nwrite", KSTAT_DATA_UINT64); + kstat_named_init(&vsp->write_bytes, "write_bytes", KSTAT_DATA_UINT64); + /* VOP_IOCTL */ + kstat_named_init(&vsp->nioctl, "nioctl", KSTAT_DATA_UINT64); + /* VOP_SETFL */ + kstat_named_init(&vsp->nsetfl, "nsetfl", KSTAT_DATA_UINT64); + /* VOP_GETATTR */ + kstat_named_init(&vsp->ngetattr, "ngetattr", KSTAT_DATA_UINT64); + /* VOP_SETATTR */ + kstat_named_init(&vsp->nsetattr, "nsetattr", KSTAT_DATA_UINT64); + /* VOP_ACCESS */ + kstat_named_init(&vsp->naccess, "naccess", KSTAT_DATA_UINT64); + /* VOP_LOOKUP */ + kstat_named_init(&vsp->nlookup, "nlookup", KSTAT_DATA_UINT64); + /* VOP_CREATE */ + kstat_named_init(&vsp->ncreate, "ncreate", KSTAT_DATA_UINT64); + /* VOP_REMOVE */ + kstat_named_init(&vsp->nremove, "nremove", KSTAT_DATA_UINT64); + /* VOP_LINK */ + kstat_named_init(&vsp->nlink, "nlink", KSTAT_DATA_UINT64); + /* VOP_RENAME */ + kstat_named_init(&vsp->nrename, "nrename", KSTAT_DATA_UINT64); + /* VOP_MKDIR */ + kstat_named_init(&vsp->nmkdir, "nmkdir", KSTAT_DATA_UINT64); + /* VOP_RMDIR */ + kstat_named_init(&vsp->nrmdir, "nrmdir", KSTAT_DATA_UINT64); + /* VOP_READDIR I/O */ + kstat_named_init(&vsp->nreaddir, "nreaddir", KSTAT_DATA_UINT64); + kstat_named_init(&vsp->readdir_bytes, "readdir_bytes", + KSTAT_DATA_UINT64); + /* VOP_SYMLINK */ + kstat_named_init(&vsp->nsymlink, "nsymlink", KSTAT_DATA_UINT64); + /* VOP_READLINK */ + kstat_named_init(&vsp->nreadlink, "nreadlink", KSTAT_DATA_UINT64); + /* VOP_FSYNC */ + kstat_named_init(&vsp->nfsync, "nfsync", KSTAT_DATA_UINT64); + /* VOP_INACTIVE */ + kstat_named_init(&vsp->ninactive, "ninactive", KSTAT_DATA_UINT64); + /* VOP_FID */ + kstat_named_init(&vsp->nfid, "nfid", KSTAT_DATA_UINT64); + /* VOP_RWLOCK */ + kstat_named_init(&vsp->nrwlock, "nrwlock", KSTAT_DATA_UINT64); + /* VOP_RWUNLOCK */ + kstat_named_init(&vsp->nrwunlock, "nrwunlock", KSTAT_DATA_UINT64); + /* VOP_SEEK */ + kstat_named_init(&vsp->nseek, "nseek", KSTAT_DATA_UINT64); + /* VOP_CMP */ + kstat_named_init(&vsp->ncmp, "ncmp", KSTAT_DATA_UINT64); + /* VOP_FRLOCK */ + kstat_named_init(&vsp->nfrlock, "nfrlock", KSTAT_DATA_UINT64); + /* VOP_SPACE */ + kstat_named_init(&vsp->nspace, "nspace", KSTAT_DATA_UINT64); + /* VOP_REALVP */ + kstat_named_init(&vsp->nrealvp, "nrealvp", KSTAT_DATA_UINT64); + /* VOP_GETPAGE */ + kstat_named_init(&vsp->ngetpage, "ngetpage", KSTAT_DATA_UINT64); + /* VOP_PUTPAGE */ + kstat_named_init(&vsp->nputpage, "nputpage", KSTAT_DATA_UINT64); + /* VOP_MAP */ + kstat_named_init(&vsp->nmap, "nmap", KSTAT_DATA_UINT64); + /* VOP_ADDMAP */ + kstat_named_init(&vsp->naddmap, "naddmap", KSTAT_DATA_UINT64); + /* VOP_DELMAP */ + kstat_named_init(&vsp->ndelmap, "ndelmap", KSTAT_DATA_UINT64); + /* VOP_POLL */ + kstat_named_init(&vsp->npoll, "npoll", KSTAT_DATA_UINT64); + /* VOP_DUMP */ + kstat_named_init(&vsp->ndump, "ndump", KSTAT_DATA_UINT64); + /* VOP_PATHCONF */ + kstat_named_init(&vsp->npathconf, "npathconf", KSTAT_DATA_UINT64); + /* VOP_PAGEIO */ + kstat_named_init(&vsp->npageio, "npageio", KSTAT_DATA_UINT64); + /* VOP_DUMPCTL */ + kstat_named_init(&vsp->ndumpctl, "ndumpctl", KSTAT_DATA_UINT64); + /* VOP_DISPOSE */ + kstat_named_init(&vsp->ndispose, "ndispose", KSTAT_DATA_UINT64); + /* VOP_SETSECATTR */ + kstat_named_init(&vsp->nsetsecattr, "nsetsecattr", KSTAT_DATA_UINT64); + /* VOP_GETSECATTR */ + kstat_named_init(&vsp->ngetsecattr, "ngetsecattr", KSTAT_DATA_UINT64); + /* VOP_SHRLOCK */ + kstat_named_init(&vsp->nshrlock, "nshrlock", KSTAT_DATA_UINT64); + /* VOP_VNEVENT */ + kstat_named_init(&vsp->nvnevent, "nvnevent", KSTAT_DATA_UINT64); + + return (vsp); +} + +/* + * Creates a kstat structure associated with a vopstats structure. + */ +kstat_t * +new_vskstat(char *ksname, vopstats_t *vsp) +{ + kstat_t *ksp; + + if (!vopstats_enabled) { + return (NULL); + } + + ksp = kstat_create("unix", 0, ksname, "misc", KSTAT_TYPE_NAMED, + sizeof (vopstats_t)/sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL|KSTAT_FLAG_WRITABLE); + if (ksp) { + ksp->ks_data = vsp; + kstat_install(ksp); + } + + return (ksp); +} + +/* + * Called from vfsinit() to initialize the support mechanisms for vopstats + */ +void +vopstats_startup() +{ + if (!vopstats_enabled) + return; + + /* + * Creates the AVL tree which holds per-vfs vopstat anchors. This + * is necessary since we need to check if a kstat exists before we + * attempt to create it. Also, initialize its lock. + */ + avl_create(&vskstat_tree, vska_compar, sizeof (vsk_anchor_t), + offsetof(vsk_anchor_t, vsk_node)); + mutex_init(&vskstat_tree_lock, NULL, MUTEX_DEFAULT, NULL); + + vsk_anchor_cache = kmem_cache_create("vsk_anchor_cache", + sizeof (vsk_anchor_t), sizeof (uintptr_t), NULL, NULL, NULL, + NULL, NULL, 0); + + /* + * Set up the array of pointers for the vopstats-by-FS-type. + * The entries will be allocated/initialized as each file system + * goes through modload/mod_installfs. + */ + vopstats_fstype = (vopstats_t **)kmem_zalloc( + (sizeof (vopstats_t *) * nfstype), KM_SLEEP); + + /* Set up the global vopstats initialization template */ + vs_templatep = create_vopstats_template(); +} + +/* + * We need to have the all of the counters zeroed. + * The initialization of the vopstats_t includes on the order of + * 50 calls to kstat_named_init(). Rather that do that on every call, + * we do it once in a template (vs_templatep) then bcopy it over. + */ +void +initialize_vopstats(vopstats_t *vsp) +{ + if (vsp == NULL) + return; + + bcopy(vs_templatep, vsp, sizeof (vopstats_t)); +} + +/* + * Create and initialize the vopstat structure for a vfs. Also, generate + * a kstat name, create the kstat structure, and associate it with the + * vfs' vopstats. This must only be called from mount. + */ +void +setup_vopstats(vfs_t *vfsp) +{ + int fstype = 0; /* Index into vfssw[] */ + char kstatstr[KSTAT_STRLEN]; /* kstat name for vopstats */ + statvfs64_t statvfsbuf; /* Needed to find f_fsid */ + vsk_anchor_t *vskp; /* vfs <--> kstat anchor */ + vfsops_t *vfsops; /* vfs operations vector */ + vfssw_t *vswp; /* Ptr into vfssw[] table */ + kstat_t *ksp; /* Ptr to new kstat */ + avl_index_t where; /* Location in the AVL tree */ + + if (vfsp == NULL || (vfsp->vfs_flag & VFS_STATS) == 0 || + !vopstats_enabled) + return; + + initialize_vopstats(&vfsp->vfs_vopstats); + + /* + * Set up the fstype. We go to so much trouble because all versions + * of NFS use the same fstype in their vfs even though they have + * distinct entries in the vfssw[] table. + */ + if (vfsp && (vfsops = vfs_getops(vfsp)) != NULL) { + vswp = vfs_getvfsswbyvfsops(vfsops); + /* A special vfs (e.g., EIO_vfs) may not have an entry */ + if (vswp) { + fstype = vswp - vfssw; /* Gets us the index */ + vfs_unrefvfssw(vswp); /* Must release reference */ + } + } else { + fstype = vfsp->vfs_fstype; + } + + /* + * Point to the per-fstype vopstats. The only valid values are + * non-zero positive values less than the number of vfssw[] table + * entries. + */ + if (fstype > 0 && fstype < nfstype) { + vfsp->vfs_fstypevsp = vopstats_fstype[fstype]; + } else { + /* Otherwise, never attempt to update stats by fstype */ + vfsp->vfs_fstypevsp = NULL; + } + + /* Need to get the fsid to build a kstat name */ + if (VFS_STATVFS(vfsp, &statvfsbuf) == 0) { + /* Create a name for our kstats based on fsid */ + (void) snprintf(kstatstr, KSTAT_STRLEN, "%s%lx", + VOPSTATS_STR, statvfsbuf.f_fsid); + + /* Allocate and initialize the vsk_anchor_t */ + vskp = kmem_cache_alloc(vsk_anchor_cache, KM_SLEEP); + bzero(vskp, sizeof (*vskp)); + vskp->vsk_fsid = statvfsbuf.f_fsid; + vfsp->vfs_vskap = vskp; + + mutex_enter(&vskstat_tree_lock); + if (avl_find(&vskstat_tree, vskp, &where) == NULL) { + avl_insert(&vskstat_tree, vskp, where); + mutex_exit(&vskstat_tree_lock); + + /* + * Now that we've got the anchor in the AVL + * tree, we can create the kstat. + */ + ksp = new_vskstat(kstatstr, &vfsp->vfs_vopstats); + if (ksp) { + vskp->vsk_ksp = ksp; + } + } else { + /* Oops, found one! Release memory and lock. */ + mutex_exit(&vskstat_tree_lock); + vfsp->vfs_vskap = NULL; + kmem_cache_free(vsk_anchor_cache, vskp); + } + } +} + +/* + * We're in the process of tearing down the vfs and need to cleanup + * the data structures associated with the vopstats. Must only be called + * from dounmount(). + */ +void +teardown_vopstats(vfs_t *vfsp) +{ + vsk_anchor_t *vskap; + avl_index_t where; + + if (vfsp == NULL || (vfsp->vfs_flag & VFS_STATS) == 0 || + !vopstats_enabled) + return; + + /* This is a safe check since VFS_STATS must be set (see above) */ + if ((vskap = vfsp->vfs_vskap) == NULL) + return; + + /* Whack the pointer right away */ + vfsp->vfs_vskap = NULL; + + /* Lock the tree, remove the node, and delete the kstat */ + mutex_enter(&vskstat_tree_lock); + if (avl_find(&vskstat_tree, vskap, &where)) { + avl_remove(&vskstat_tree, vskap); + } + + if (vskap->vsk_ksp) { + kstat_delete(vskap->vsk_ksp); + } + mutex_exit(&vskstat_tree_lock); + + kmem_cache_free(vsk_anchor_cache, vskap); +} /* * Read or write a vnode. Called from kernel code. @@ -2307,6 +2693,7 @@ * Use the saved vp just in case the vnode ptr got trashed * by the error. */ + VOPSTATS_UPDATE(vp, nopen); if ((vp->v_type == VREG) && (mode & FREAD)) atomic_add_32(&(vp->v_rdcnt), -1); if ((vp->v_type == VREG) && (mode & FWRITE)) @@ -2320,7 +2707,7 @@ * casing each filesystem. Adjust the vnode counts to * reflect the vnode switch. */ - + VOPSTATS_UPDATE(*vpp, nopen); if (*vpp != vp && *vpp != NULL) { vn_copypath(vp, *vpp); if (((*vpp)->v_type == VREG) && (mode & FREAD)) @@ -2345,8 +2732,10 @@ offset_t offset, cred_t *cr) { - int error; - error = (*(vp)->v_op->vop_close)(vp, flag, count, offset, cr); + int err; + + err = (*(vp)->v_op->vop_close)(vp, flag, count, offset, cr); + VOPSTATS_UPDATE(vp, nclose); /* * Check passed in count to handle possible dups. Vnode counts are only * kept on regular files @@ -2361,7 +2750,7 @@ atomic_add_32(&(vp->v_wrcnt), -1); } } - return (error); + return (err); } int @@ -2372,7 +2761,13 @@ cred_t *cr, struct caller_context *ct) { - return (*(vp)->v_op->vop_read)(vp, uiop, ioflag, cr, ct); + int err; + ssize_t resid_start = uiop->uio_resid; + + err = (*(vp)->v_op->vop_read)(vp, uiop, ioflag, cr, ct); + VOPSTATS_UPDATE_IO(vp, nread, + read_bytes, (resid_start - uiop->uio_resid)); + return (err); } int @@ -2383,7 +2778,13 @@ cred_t *cr, struct caller_context *ct) { - return (*(vp)->v_op->vop_write)(vp, uiop, ioflag, cr, ct); + int err; + ssize_t resid_start = uiop->uio_resid; + + err = (*(vp)->v_op->vop_write)(vp, uiop, ioflag, cr, ct); + VOPSTATS_UPDATE_IO(vp, nwrite, + write_bytes, (resid_start - uiop->uio_resid)); + return (err); } int @@ -2395,7 +2796,11 @@ cred_t *cr, int *rvalp) { - return (*(vp)->v_op->vop_ioctl)(vp, cmd, arg, flag, cr, rvalp); + int err; + + err = (*(vp)->v_op->vop_ioctl)(vp, cmd, arg, flag, cr, rvalp); + VOPSTATS_UPDATE(vp, nioctl); + return (err); } int @@ -2405,7 +2810,11 @@ int nflags, cred_t *cr) { - return (*(vp)->v_op->vop_setfl)(vp, oflags, nflags, cr); + int err; + + err = (*(vp)->v_op->vop_setfl)(vp, oflags, nflags, cr); + VOPSTATS_UPDATE(vp, nsetfl); + return (err); } int @@ -2415,7 +2824,11 @@ int flags, cred_t *cr) { - return (*(vp)->v_op->vop_getattr)(vp, vap, flags, cr); + int err; + + err = (*(vp)->v_op->vop_getattr)(vp, vap, flags, cr); + VOPSTATS_UPDATE(vp, ngetattr); + return (err); } int @@ -2426,7 +2839,11 @@ cred_t *cr, caller_context_t *ct) { - return (*(vp)->v_op->vop_setattr)(vp, vap, flags, cr, ct); + int err; + + err = (*(vp)->v_op->vop_setattr)(vp, vap, flags, cr, ct); + VOPSTATS_UPDATE(vp, nsetattr); + return (err); } int @@ -2436,7 +2853,11 @@ int flags, cred_t *cr) { - return (*(vp)->v_op->vop_access)(vp, mode, flags, cr); + int err; + + err = (*(vp)->v_op->vop_access)(vp, mode, flags, cr); + VOPSTATS_UPDATE(vp, naccess); + return (err); } int @@ -2452,8 +2873,12 @@ int ret; ret = (*(dvp)->v_op->vop_lookup)(dvp, nm, vpp, pnp, flags, rdir, cr); - if (ret == 0 && *vpp && (*vpp)->v_path == NULL) - vn_setpath(rootdir, dvp, *vpp, nm, strlen(nm)); + if (ret == 0 && *vpp) { + VOPSTATS_UPDATE(*vpp, nlookup); + if ((*vpp)->v_path == NULL) { + vn_setpath(rootdir, dvp, *vpp, nm, strlen(nm)); + } + } return (ret); } @@ -2473,8 +2898,12 @@ ret = (*(dvp)->v_op->vop_create) (dvp, name, vap, excl, mode, vpp, cr, flag); - if (ret == 0 && *vpp && (*vpp)->v_path == NULL) - vn_setpath(rootdir, dvp, *vpp, name, strlen(name)); + if (ret == 0 && *vpp) { + VOPSTATS_UPDATE(*vpp, ncreate); + if ((*vpp)->v_path == NULL) { + vn_setpath(rootdir, dvp, *vpp, name, strlen(name)); + } + } return (ret); } @@ -2485,7 +2914,11 @@ char *nm, cred_t *cr) { - return (*(dvp)->v_op->vop_remove)(dvp, nm, cr); + int err; + + err = (*(dvp)->v_op->vop_remove)(dvp, nm, cr); + VOPSTATS_UPDATE(dvp, nremove); + return (err); } int @@ -2495,7 +2928,11 @@ char *tnm, cred_t *cr) { - return (*(tdvp)->v_op->vop_link)(tdvp, svp, tnm, cr); + int err; + + err = (*(tdvp)->v_op->vop_link)(tdvp, svp, tnm, cr); + VOPSTATS_UPDATE(tdvp, nlink); + return (err); } int @@ -2506,7 +2943,11 @@ char *tnm, cred_t *cr) { - return (*(sdvp)->v_op->vop_rename)(sdvp, snm, tdvp, tnm, cr); + int err; + + err = (*(sdvp)->v_op->vop_rename)(sdvp, snm, tdvp, tnm, cr); + VOPSTATS_UPDATE(sdvp, nrename); + return (err); } int @@ -2520,8 +2961,13 @@ int ret; ret = (*(dvp)->v_op->vop_mkdir)(dvp, dirname, vap, vpp, cr); - if (ret == 0 && *vpp && (*vpp)->v_path == NULL) - vn_setpath(rootdir, dvp, *vpp, dirname, strlen(dirname)); + if (ret == 0 && *vpp) { + VOPSTATS_UPDATE(*vpp, nmkdir); + if ((*vpp)->v_path == NULL) { + vn_setpath(rootdir, dvp, *vpp, dirname, + strlen(dirname)); + } + } return (ret); } @@ -2533,7 +2979,11 @@ vnode_t *cdir, cred_t *cr) { - return (*(dvp)->v_op->vop_rmdir)(dvp, nm, cdir, cr); + int err; + + err = (*(dvp)->v_op->vop_rmdir)(dvp, nm, cdir, cr); + VOPSTATS_UPDATE(dvp, nrmdir); + return (err); } int @@ -2543,7 +2993,13 @@ cred_t *cr, int *eofp) { - return (*(vp)->v_op->vop_readdir)(vp, uiop, cr, eofp); + int err; + ssize_t resid_start = uiop->uio_resid; + + err = (*(vp)->v_op->vop_readdir)(vp, uiop, cr, eofp); + VOPSTATS_UPDATE_IO(vp, nreaddir, + readdir_bytes, (resid_start - uiop->uio_resid)); + return (err); } int @@ -2554,7 +3010,11 @@ char *target, cred_t *cr) { - return (*(dvp)->v_op->vop_symlink) (dvp, linkname, vap, target, cr); + int err; + + err = (*(dvp)->v_op->vop_symlink) (dvp, linkname, vap, target, cr); + VOPSTATS_UPDATE(dvp, nsymlink); + return (err); } int @@ -2563,7 +3023,11 @@ uio_t *uiop, cred_t *cr) { - return (*(vp)->v_op->vop_readlink)(vp, uiop, cr); + int err; + + err = (*(vp)->v_op->vop_readlink)(vp, uiop, cr); + VOPSTATS_UPDATE(vp, nreadlink); + return (err); } int @@ -2572,7 +3036,11 @@ int syncflag, cred_t *cr) { - return (*(vp)->v_op->vop_fsync)(vp, syncflag, cr); + int err; + + err = (*(vp)->v_op->vop_fsync)(vp, syncflag, cr); + VOPSTATS_UPDATE(vp, nfsync); + return (err); } void @@ -2580,6 +3048,8 @@ vnode_t *vp, cred_t *cr) { + /* Need to update stats before vop call since we may lose the vnode */ + VOPSTATS_UPDATE(vp, ninactive); (*(vp)->v_op->vop_inactive)(vp, cr); } @@ -2588,7 +3058,11 @@ vnode_t *vp, fid_t *fidp) { - return (*(vp)->v_op->vop_fid)(vp, fidp); + int err; + + err = (*(vp)->v_op->vop_fid)(vp, fidp); + VOPSTATS_UPDATE(vp, nfid); + return (err); } int @@ -2597,7 +3071,11 @@ int write_lock, caller_context_t *ct) { - return ((*(vp)->v_op->vop_rwlock)(vp, write_lock, ct)); + int ret; + + ret = ((*(vp)->v_op->vop_rwlock)(vp, write_lock, ct)); + VOPSTATS_UPDATE(vp, nrwlock); + return (ret); } void @@ -2607,6 +3085,7 @@ caller_context_t *ct) { (*(vp)->v_op->vop_rwunlock)(vp, write_lock, ct); + VOPSTATS_UPDATE(vp, nrwunlock); } int @@ -2615,7 +3094,11 @@ offset_t ooff, offset_t *noffp) { - return (*(vp)->v_op->vop_seek)(vp, ooff, noffp); + int err; + + err = (*(vp)->v_op->vop_seek)(vp, ooff, noffp); + VOPSTATS_UPDATE(vp, nseek); + return (err); } int @@ -2623,7 +3106,11 @@ vnode_t *vp1, vnode_t *vp2) { - return (*(vp1)->v_op->vop_cmp)(vp1, vp2); + int err; + + err = (*(vp1)->v_op->vop_cmp)(vp1, vp2); + VOPSTATS_UPDATE(vp1, ncmp); + return (err); } int @@ -2636,8 +3123,12 @@ struct flk_callback *flk_cbp, cred_t *cr) { - return (*(vp)->v_op->vop_frlock) + int err; + + err = (*(vp)->v_op->vop_frlock) (vp, cmd, bfp, flag, offset, flk_cbp, cr); + VOPSTATS_UPDATE(vp, nfrlock); + return (err); } int @@ -2650,7 +3141,11 @@ cred_t *cr, caller_context_t *ct) { - return (*(vp)->v_op->vop_space)(vp, cmd, bfp, flag, offset, cr, ct); + int err; + + err = (*(vp)->v_op->vop_space)(vp, cmd, bfp, flag, offset, cr, ct); + VOPSTATS_UPDATE(vp, nspace); + return (err); } int @@ -2658,7 +3153,11 @@ vnode_t *vp, vnode_t **vpp) { - return (*(vp)->v_op->vop_realvp)(vp, vpp); + int err; + + err = (*(vp)->v_op->vop_realvp)(vp, vpp); + VOPSTATS_UPDATE(vp, nrealvp); + return (err); } int @@ -2674,8 +3173,12 @@ enum seg_rw rw, cred_t *cr) { - return (*(vp)->v_op->vop_getpage) + int err; + + err = (*(vp)->v_op->vop_getpage) (vp, off, len, protp, plarr, plsz, seg, addr, rw, cr); + VOPSTATS_UPDATE(vp, ngetpage); + return (err); } int @@ -2686,7 +3189,11 @@ int flags, cred_t *cr) { - return (*(vp)->v_op->vop_putpage)(vp, off, len, flags, cr); + int err; + + err = (*(vp)->v_op->vop_putpage)(vp, off, len, flags, cr); + VOPSTATS_UPDATE(vp, nputpage); + return (err); } int @@ -2701,8 +3208,12 @@ uint_t flags, cred_t *cr) { - return (*(vp)->v_op->vop_map) + int err; + + err = (*(vp)->v_op->vop_map) (vp, off, as, addrp, len, prot, maxprot, flags, cr); + VOPSTATS_UPDATE(vp, nmap); + return (err); } int @@ -2748,6 +3259,7 @@ (int64_t)delta); } } + VOPSTATS_UPDATE(vp, naddmap); return (error); } @@ -2798,6 +3310,7 @@ (int64_t)(-delta)); } } + VOPSTATS_UPDATE(vp, ndelmap); return (error); } @@ -2810,7 +3323,11 @@ short *reventsp, struct pollhead **phpp) { - return (*(vp)->v_op->vop_poll)(vp, events, anyyet, reventsp, phpp); + int err; + + err = (*(vp)->v_op->vop_poll)(vp, events, anyyet, reventsp, phpp); + VOPSTATS_UPDATE(vp, npoll); + return (err); } int @@ -2820,7 +3337,11 @@ int lbdn, int dblks) { - return (*(vp)->v_op->vop_dump)(vp, addr, lbdn, dblks); + int err; + + err = (*(vp)->v_op->vop_dump)(vp, addr, lbdn, dblks); + VOPSTATS_UPDATE(vp, ndump); + return (err); } int @@ -2830,7 +3351,11 @@ ulong_t *valp, cred_t *cr) { - return (*(vp)->v_op->vop_pathconf)(vp, cmd, valp, cr); + int err; + + err = (*(vp)->v_op->vop_pathconf)(vp, cmd, valp, cr); + VOPSTATS_UPDATE(vp, npathconf); + return (err); } int @@ -2842,7 +3367,11 @@ int flags, cred_t *cr) { - return (*(vp)->v_op->vop_pageio)(vp, pp, io_off, io_len, flags, cr); + int err; + + err = (*(vp)->v_op->vop_pageio)(vp, pp, io_off, io_len, flags, cr); + VOPSTATS_UPDATE(vp, npageio); + return (err); } int @@ -2851,7 +3380,10 @@ int action, int *blkp) { - return (*(vp)->v_op->vop_dumpctl)(vp, action, blkp); + int err; + err = (*(vp)->v_op->vop_dumpctl)(vp, action, blkp); + VOPSTATS_UPDATE(vp, ndumpctl); + return (err); } void @@ -2862,6 +3394,8 @@ int dn, cred_t *cr) { + /* Must do stats first since it's possible to lose the vnode */ + VOPSTATS_UPDATE(vp, ndispose); (*(vp)->v_op->vop_dispose)(vp, pp, flag, dn, cr); } @@ -2872,7 +3406,11 @@ int flag, cred_t *cr) { - return (*(vp)->v_op->vop_setsecattr) (vp, vsap, flag, cr); + int err; + + err = (*(vp)->v_op->vop_setsecattr) (vp, vsap, flag, cr); + VOPSTATS_UPDATE(vp, nsetsecattr); + return (err); } int @@ -2882,7 +3420,11 @@ int flag, cred_t *cr) { - return (*(vp)->v_op->vop_getsecattr) (vp, vsap, flag, cr); + int err; + + err = (*(vp)->v_op->vop_getsecattr) (vp, vsap, flag, cr); + VOPSTATS_UPDATE(vp, ngetsecattr); + return (err); } int @@ -2893,11 +3435,19 @@ int flag, cred_t *cr) { - return (*(vp)->v_op->vop_shrlock)(vp, cmd, shr, flag, cr); + int err; + + err = (*(vp)->v_op->vop_shrlock)(vp, cmd, shr, flag, cr); + VOPSTATS_UPDATE(vp, nshrlock); + return (err); } int fop_vnevent(vnode_t *vp, vnevent_t vnevent) { - return (*(vp)->v_op->vop_vnevent)(vp, vnevent); + int err; + + err = (*(vp)->v_op->vop_vnevent)(vp, vnevent); + VOPSTATS_UPDATE(vp, nvnevent); + return (err); }
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c Sat Feb 25 01:33:06 2006 -0800 @@ -1072,7 +1072,7 @@ VFSDEF_VERSION, MNTTYPE_ZFS, zfs_vfsinit, - VSW_HASPROTO | VSW_CANRWRO | VSW_CANREMOUNT | VSW_VOLATILEDEV, + VSW_HASPROTO|VSW_CANRWRO|VSW_CANREMOUNT|VSW_VOLATILEDEV|VSW_STATS, &zfs_mntopts };
--- a/usr/src/uts/common/os/modconf.c Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/os/modconf.c Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -34,6 +33,7 @@ #include <sys/conf.h> #include <sys/class.h> #include <sys/vfs.h> +#include <sys/vnode.h> #include <sys/mount.h> #include <sys/systm.h> #include <sys/modctl.h> @@ -56,6 +56,7 @@ #include <sys/strsubr.h> #include <sys/kcpc.h> #include <sys/cpc_pcbe.h> +#include <sys/kstat.h> extern int moddebug; @@ -890,8 +891,16 @@ struct vfssw *vswp; struct modctl *mcp; char *fsname; + char ksname[KSTAT_STRLEN + 1]; + int fstype; /* index into vfssw[] and vsanchor_fstype[] */ int allocated; int err; + int vsw_stats_enabled; + /* Not for public consumption so these aren't in a header file */ + extern int vopstats_enabled; + extern vopstats_t **vopstats_fstype; + extern kstat_t *new_vskstat(char *, vopstats_t *); + extern void initialize_vopstats(vopstats_t *); if (modl->fs_vfsdef->def_version == VFSDEF_VERSION) { /* Version matched */ @@ -931,7 +940,11 @@ } ASSERT(vswp != NULL); - vswp->vsw_flag = modl->fs_vfsdef->flags; + fstype = vswp - vfssw; /* Pointer arithmetic to get the fstype */ + + /* Turn on everything by default *except* VSW_STATS */ + vswp->vsw_flag = modl->fs_vfsdef->flags & ~(VSW_STATS); + if (modl->fs_vfsdef->flags & VSW_HASPROTO) { vfs_mergeopttbl(&vfs_mntopts, modl->fs_vfsdef->optproto, &vswp->vsw_optproto); @@ -945,10 +958,19 @@ */ vswp->vsw_flag |= VSW_CANREMOUNT; } + + /* + * If stats are enabled system wide and for this fstype, then + * set the VSW_STATS flag in the proper vfssw[] table entry. + */ + if (vopstats_enabled && modl->fs_vfsdef->flags & VSW_STATS) { + vswp->vsw_flag |= VSW_STATS; + } + if (modl->fs_vfsdef->init == NULL) err = EFAULT; else - err = (*(modl->fs_vfsdef->init))(vswp - vfssw, fsname); + err = (*(modl->fs_vfsdef->init))(fstype, fsname); if (err != 0) { if (allocated) { @@ -959,9 +981,22 @@ vswp->vsw_init = NULL; } + /* We don't want to hold the vfssw[] write lock over a kmem_alloc() */ + vsw_stats_enabled = vswp->vsw_flag & VSW_STATS; + vfs_unrefvfssw(vswp); WUNLOCK_VFSSW(); + /* If everything is on, set up the per-fstype vopstats */ + if (vsw_stats_enabled && vopstats_enabled && + vopstats_fstype && vopstats_fstype[fstype] == NULL) { + (void) strlcpy(ksname, VOPSTATS_STR, sizeof (ksname)); + (void) strlcat(ksname, vfssw[fstype].vsw_name, sizeof (ksname)); + vopstats_fstype[fstype] = + kmem_alloc(sizeof (vopstats_t), KM_SLEEP); + initialize_vopstats(vopstats_fstype[fstype]); + (void) new_vskstat(ksname, vopstats_fstype[fstype]); + } return (err); }
--- a/usr/src/uts/common/sys/vfs.h Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/sys/vfs.h Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -157,6 +156,24 @@ } mntopts_t; /* + * The kstat structures associated with the vopstats are kept in an + * AVL tree. This is to avoid the case where a file system does not + * use a unique fsid_t for each vfs (e.g., namefs). In order to do + * this, we need a structure that the AVL tree can use that also + * references the kstat. + * Note that the vks_fsid is generated from the value reported by + * VFS_STATVFS(). + */ +typedef struct vskstat_anchor { + avl_node_t vsk_node; /* Required for use by AVL routines */ + kstat_t *vsk_ksp; /* kstat structure for vopstats */ + ulong_t vsk_fsid; /* fsid associated w/this FS */ +} vsk_anchor_t; + +extern avl_tree_t vskstat_tree; +extern kmutex_t vskstat_tree_lock; + +/* * Structure per mounted file system. Each mounted file system has * an array of operations and an instance record. * @@ -215,6 +232,12 @@ struct zone *vfs_zone; /* zone that owns the mount */ struct vfs *vfs_zone_next; /* next VFS visible in zone */ struct vfs *vfs_zone_prev; /* prev VFS visible in zone */ + /* + * Support for statistics on the vnode operations + */ + vsk_anchor_t *vfs_vskap; /* anchor for vopstats' kstat */ + vopstats_t *vfs_fstypevsp; /* ptr to per-fstype vopstats */ + vopstats_t vfs_vopstats; /* per-mount vnode op stats */ } vfs_t; /* @@ -232,6 +255,7 @@ #define VFS_XATTR 0x400 /* fs supports extended attributes */ #define VFS_NODEVICES 0x800 /* device-special files disallowed */ #define VFS_NOEXEC 0x1000 /* executables disallowed */ +#define VFS_STATS 0x2000 /* file system can collect stats */ #define VFS_NORESOURCE "unspecified_resource" #define VFS_NOMNTPT "unspecified_mountpoint" @@ -274,8 +298,6 @@ typedef enum vntrans vntrans_t; - - /* * Operations supported on virtual file system. */ @@ -374,6 +396,7 @@ #define VSW_CANREMOUNT 0x04 /* file system supports remounts */ #define VSW_NOTZONESAFE 0x08 /* zone_enter(2) should fail for these files */ #define VSW_VOLATILEDEV 0x10 /* vfs_dev can change each time fs is mounted */ +#define VSW_STATS 0x20 /* file system can collect stats */ #define VSW_INSTALLED 0x8000 /* this vsw is associated with a file system */
--- a/usr/src/uts/common/sys/vnode.h Sat Feb 25 00:24:50 2006 -0800 +++ b/usr/src/uts/common/sys/vnode.h Sat Feb 25 01:33:06 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -50,6 +49,9 @@ #include <sys/uio.h> #include <sys/resource.h> #include <vm/seg_enum.h> +#include <sys/kstat.h> +#include <sys/kmem.h> +#include <sys/avl.h> #ifdef _KERNEL #include <sys/buf.h> #endif /* _KERNEL */ @@ -94,6 +96,68 @@ /* + * Statistics for all vnode operations. + * All operations record number of ops (since boot/mount/zero'ed). + * Certain I/O operations (read, write, readdir) also record number + * of bytes transferred. + * This appears in two places in the system: one is embedded in each + * vfs_t. There is also an array of vopstats_t structures allocated + * on a per-fstype basis. + */ + +#define VOPSTATS_STR "vopstats_" /* Initial string for vopstat kstats */ + +typedef struct vopstats { + kstat_named_t nopen; /* VOP_OPEN */ + kstat_named_t nclose; /* VOP_CLOSE */ + kstat_named_t nread; /* VOP_READ */ + kstat_named_t read_bytes; + kstat_named_t nwrite; /* VOP_WRITE */ + kstat_named_t write_bytes; + kstat_named_t nioctl; /* VOP_IOCTL */ + kstat_named_t nsetfl; /* VOP_SETFL */ + kstat_named_t ngetattr; /* VOP_GETATTR */ + kstat_named_t nsetattr; /* VOP_SETATTR */ + kstat_named_t naccess; /* VOP_ACCESS */ + kstat_named_t nlookup; /* VOP_LOOKUP */ + kstat_named_t ncreate; /* VOP_CREATE */ + kstat_named_t nremove; /* VOP_REMOVE */ + kstat_named_t nlink; /* VOP_LINK */ + kstat_named_t nrename; /* VOP_RENAME */ + kstat_named_t nmkdir; /* VOP_MKDIR */ + kstat_named_t nrmdir; /* VOP_RMDIR */ + kstat_named_t nreaddir; /* VOP_READDIR */ + kstat_named_t readdir_bytes; + kstat_named_t nsymlink; /* VOP_SYMLINK */ + kstat_named_t nreadlink; /* VOP_READLINK */ + kstat_named_t nfsync; /* VOP_FSYNC */ + kstat_named_t ninactive; /* VOP_INACTIVE */ + kstat_named_t nfid; /* VOP_FID */ + kstat_named_t nrwlock; /* VOP_RWLOCK */ + kstat_named_t nrwunlock; /* VOP_RWUNLOCK */ + kstat_named_t nseek; /* VOP_SEEK */ + kstat_named_t ncmp; /* VOP_CMP */ + kstat_named_t nfrlock; /* VOP_FRLOCK */ + kstat_named_t nspace; /* VOP_SPACE */ + kstat_named_t nrealvp; /* VOP_REALVP */ + kstat_named_t ngetpage; /* VOP_GETPAGE */ + kstat_named_t nputpage; /* VOP_PUTPAGE */ + kstat_named_t nmap; /* VOP_MAP */ + kstat_named_t naddmap; /* VOP_ADDMAP */ + kstat_named_t ndelmap; /* VOP_DELMAP */ + kstat_named_t npoll; /* VOP_POLL */ + kstat_named_t ndump; /* VOP_DUMP */ + kstat_named_t npathconf; /* VOP_PATHCONF */ + kstat_named_t npageio; /* VOP_PAGEIO */ + kstat_named_t ndumpctl; /* VOP_DUMPCTL */ + kstat_named_t ndispose; /* VOP_DISPOSE */ + kstat_named_t nsetsecattr; /* VOP_SETSECATTR */ + kstat_named_t ngetsecattr; /* VOP_GETSECATTR */ + kstat_named_t nshrlock; /* VOP_SHRLOCK */ + kstat_named_t nvnevent; /* VOP_VNEVENT */ +} vopstats_t; + +/* * The vnode is the focus of all file activity in UNIX. * A vnode is allocated for each active file, each current * directory, each mounted-on file, and the root.