summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/stat/Makefile9
-rw-r--r--usr/src/cmd/stat/fsstat/Makefile56
-rw-r--r--usr/src/cmd/stat/fsstat/fsstat.c1079
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com1
-rw-r--r--usr/src/uts/common/fs/autofs/auto_vfsops.c9
-rw-r--r--usr/src/uts/common/fs/hsfs/hsfs_vfsops.c4
-rw-r--r--usr/src/uts/common/fs/lofs/lofs_vfsops.c9
-rw-r--r--usr/src/uts/common/fs/mntfs/mntvfsops.c9
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_common.c9
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_common.c11
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_vfsops.c9
-rw-r--r--usr/src/uts/common/fs/proc/prvfsops.c9
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_vfsops.c9
-rw-r--r--usr/src/uts/common/fs/udfs/udf_vfsops.c9
-rw-r--r--usr/src/uts/common/fs/ufs/ufs_vfsops.c9
-rw-r--r--usr/src/uts/common/fs/vfs.c49
-rw-r--r--usr/src/uts/common/fs/vnode.c646
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vfsops.c2
-rw-r--r--usr/src/uts/common/os/modconf.c45
-rw-r--r--usr/src/uts/common/sys/vfs.h35
-rw-r--r--usr/src/uts/common/sys/vnode.h72
21 files changed, 1961 insertions, 129 deletions
diff --git a/usr/src/cmd/stat/Makefile b/usr/src/cmd/stat/Makefile
index 0bffef1465..d8b93f080a 100644
--- a/usr/src/cmd/stat/Makefile
+++ b/usr/src/cmd/stat/Makefile
@@ -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
diff --git a/usr/src/cmd/stat/fsstat/Makefile b/usr/src/cmd/stat/fsstat/Makefile
new file mode 100644
index 0000000000..235909c508
--- /dev/null
+++ b/usr/src/cmd/stat/fsstat/Makefile
@@ -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
diff --git a/usr/src/cmd/stat/fsstat/fsstat.c b/usr/src/cmd/stat/fsstat/fsstat.c
new file mode 100644
index 0000000000..510823a11d
--- /dev/null
+++ b/usr/src/cmd/stat/fsstat/fsstat.c
@@ -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);
+}
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index 162e19c3a2..23d350314e 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -127,6 +127,7 @@ f none usr/bin/fmli 755 root bin
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
diff --git a/usr/src/uts/common/fs/autofs/auto_vfsops.c b/usr/src/uts/common/fs/autofs/auto_vfsops.c
index 5232aefa0c..ff8eee15cd 100644
--- a/usr/src/uts/common/fs/autofs/auto_vfsops.c
+++ b/usr/src/uts/common/fs/autofs/auto_vfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"autofs",
autofs_init,
- VSW_HASPROTO|VSW_CANRWRO|VSW_CANREMOUNT,
+ VSW_HASPROTO|VSW_CANRWRO|VSW_CANREMOUNT|VSW_STATS,
&auto_mntopts
};
diff --git a/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c b/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c
index e1e4daae1f..e7305c2a4e 100644
--- a/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c
+++ b/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"hsfs",
hsfsinit,
- VSW_HASPROTO, /* We don't suppport remounting */
+ VSW_HASPROTO|VSW_STATS, /* We don't suppport remounting */
&hsfs_proto_opttbl
};
diff --git a/usr/src/uts/common/fs/lofs/lofs_vfsops.c b/usr/src/uts/common/fs/lofs/lofs_vfsops.c
index e5a74354be..d06f661005 100644
--- a/usr/src/uts/common/fs/lofs/lofs_vfsops.c
+++ b/usr/src/uts/common/fs/lofs/lofs_vfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"lofs",
lofsinit,
- VSW_HASPROTO,
+ VSW_HASPROTO|VSW_STATS,
&lofs_mntopts
};
diff --git a/usr/src/uts/common/fs/mntfs/mntvfsops.c b/usr/src/uts/common/fs/mntfs/mntvfsops.c
index 59b683a1bc..f4d679fc9e 100644
--- a/usr/src/uts/common/fs/mntfs/mntvfsops.c
+++ b/usr/src/uts/common/fs/mntfs/mntvfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"mntfs",
mntinit,
- VSW_HASPROTO,
+ VSW_HASPROTO|VSW_STATS,
&mnt_mntopts
};
diff --git a/usr/src/uts/common/fs/nfs/nfs4_common.c b/usr/src/uts/common/fs/nfs/nfs4_common.c
index 7e89246141..4966d48946 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_common.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_common.c
@@ -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 @@ static vfsdef_t vfw4 = {
VFSDEF_VERSION,
"nfs4",
nfs4init,
- VSW_CANREMOUNT|VSW_NOTZONESAFE,
+ VSW_CANREMOUNT|VSW_NOTZONESAFE|VSW_STATS,
NULL
};
diff --git a/usr/src/uts/common/fs/nfs/nfs_common.c b/usr/src/uts/common/fs/nfs/nfs_common.c
index 6007cc8a46..3d3eecd82a 100644
--- a/usr/src/uts/common/fs/nfs/nfs_common.c
+++ b/usr/src/uts/common/fs/nfs/nfs_common.c
@@ -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 @@ static vfsdef_t vfw2 = {
VFSDEF_VERSION,
"nfs",
nfsinit,
- VSW_CANREMOUNT|VSW_NOTZONESAFE,
+ VSW_CANREMOUNT|VSW_NOTZONESAFE|VSW_STATS,
NULL
};
@@ -159,7 +158,7 @@ static vfsdef_t vfw3 = {
VFSDEF_VERSION,
"nfs3",
nfs3init,
- VSW_CANREMOUNT|VSW_NOTZONESAFE,
+ VSW_CANREMOUNT|VSW_NOTZONESAFE|VSW_STATS,
NULL
};
diff --git a/usr/src/uts/common/fs/pcfs/pc_vfsops.c b/usr/src/uts/common/fs/pcfs/pc_vfsops.c
index 4b376ffc0f..881e75f99b 100644
--- a/usr/src/uts/common/fs/pcfs/pc_vfsops.c
+++ b/usr/src/uts/common/fs/pcfs/pc_vfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"pcfs",
pcfsinit,
- VSW_HASPROTO|VSW_CANREMOUNT,
+ VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS,
&pcfs_mntopts
};
diff --git a/usr/src/uts/common/fs/proc/prvfsops.c b/usr/src/uts/common/fs/proc/prvfsops.c
index 3e8ee46ab3..8f2d079feb 100644
--- a/usr/src/uts/common/fs/proc/prvfsops.c
+++ b/usr/src/uts/common/fs/proc/prvfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"proc",
prinit,
- VSW_HASPROTO,
+ VSW_HASPROTO|VSW_STATS,
&proc_mntopts
};
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
index fa31fe3b08..fb5e0d74e4 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"tmpfs",
tmpfsinit,
- VSW_HASPROTO,
+ VSW_HASPROTO|VSW_STATS,
&tmpfs_proto_opttbl
};
diff --git a/usr/src/uts/common/fs/udfs/udf_vfsops.c b/usr/src/uts/common/fs/udfs/udf_vfsops.c
index e7aa9ba44c..0ff009d6f3 100644
--- a/usr/src/uts/common/fs/udfs/udf_vfsops.c
+++ b/usr/src/uts/common/fs/udfs/udf_vfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"udfs",
udfinit,
- VSW_HASPROTO|VSW_CANREMOUNT,
+ VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS,
&udfs_mntopts
};
diff --git a/usr/src/uts/common/fs/ufs/ufs_vfsops.c b/usr/src/uts/common/fs/ufs/ufs_vfsops.c
index f205f3d2e5..78c9f4b17e 100644
--- a/usr/src/uts/common/fs/ufs/ufs_vfsops.c
+++ b/usr/src/uts/common/fs/ufs/ufs_vfsops.c
@@ -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 @@ static vfsdef_t vfw = {
VFSDEF_VERSION,
"ufs",
ufsinit,
- VSW_HASPROTO|VSW_CANREMOUNT,
+ VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS,
&ufs_mntopts
};
diff --git a/usr/src/uts/common/fs/vfs.c b/usr/src/uts/common/fs/vfs.c
index c6974ac42a..771fedf44d 100644
--- a/usr/src/uts/common/fs/vfs.c
+++ b/usr/src/uts/common/fs/vfs.c
@@ -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 @@ vfs_init(vfs_t *vfsp, vfsops_t *op, void *data)
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 @@ vfs_mountroot(void)
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 @@ vfs_mountroot(void)
*/
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 @@ domount(char *fsname, struct mounta *uap, vnode_t *vp, struct cred *credp,
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 @@ domount(char *fsname, struct mounta *uap, vnode_t *vp, struct cred *credp,
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 @@ dounmount(struct vfs *vfsp, int flag, cred_t *cr)
{
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 @@ dounmount(struct vfs *vfsp, int flag, cred_t *cr)
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 @@ dounmount(struct vfs *vfsp, int flag, cred_t *cr)
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 @@ vfsinit(void)
{
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 @@ vfsinit(void)
(*vswp->vsw_init)(vswp - vfssw, vswp->vsw_name);
RUNLOCK_VFSSW();
}
+
+ vopstats_startup();
+ setup_vopstats(&EIO_vfs);
}
/*
diff --git a/usr/src/uts/common/fs/vnode.c b/usr/src/uts/common/fs/vnode.c
index 4d562852af..66be3f73b9 100644
--- a/usr/src/uts/common/fs/vnode.c
+++ b/usr/src/uts/common/fs/vnode.c
@@ -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 @@ static const fs_operation_trans_def_t vn_ops_table[] = {
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 @@ fop_open(
* 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 @@ fop_open(
* 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 @@ fop_close(
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 @@ fop_close(
atomic_add_32(&(vp->v_wrcnt), -1);
}
}
- return (error);
+ return (err);
}
int
@@ -2372,7 +2761,13 @@ fop_read(
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 @@ fop_write(
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 @@ fop_ioctl(
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 @@ fop_setfl(
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 @@ fop_getattr(
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 @@ fop_setattr(
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 @@ fop_access(
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 @@ fop_lookup(
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 @@ fop_create(
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 @@ fop_remove(
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 @@ fop_link(
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 @@ fop_rename(
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 @@ fop_mkdir(
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 @@ fop_rmdir(
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 @@ fop_readdir(
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 @@ fop_symlink(
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 @@ fop_readlink(
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 @@ fop_fsync(
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 @@ fop_inactive(
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 @@ fop_fid(
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 @@ fop_rwlock(
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 @@ fop_rwunlock(
caller_context_t *ct)
{
(*(vp)->v_op->vop_rwunlock)(vp, write_lock, ct);
+ VOPSTATS_UPDATE(vp, nrwunlock);
}
int
@@ -2615,7 +3094,11 @@ fop_seek(
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 @@ fop_cmp(
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 @@ fop_frlock(
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 @@ fop_space(
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 @@ fop_realvp(
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 @@ fop_getpage(
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 @@ fop_putpage(
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 @@ fop_map(
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 @@ fop_addmap(
(int64_t)delta);
}
}
+ VOPSTATS_UPDATE(vp, naddmap);
return (error);
}
@@ -2798,6 +3310,7 @@ fop_delmap(
(int64_t)(-delta));
}
}
+ VOPSTATS_UPDATE(vp, ndelmap);
return (error);
}
@@ -2810,7 +3323,11 @@ fop_poll(
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 @@ fop_dump(
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 @@ fop_pathconf(
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 @@ fop_pageio(
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 @@ fop_dumpctl(
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 @@ fop_dispose(
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 @@ fop_setsecattr(
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 @@ fop_getsecattr(
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 @@ fop_shrlock(
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);
}
diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
index f9261592f1..17771b2e26 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
@@ -1072,7 +1072,7 @@ static vfsdef_t vfw = {
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
};
diff --git a/usr/src/uts/common/os/modconf.c b/usr/src/uts/common/os/modconf.c
index f270cad2a0..35eb1c6825 100644
--- a/usr/src/uts/common/os/modconf.c
+++ b/usr/src/uts/common/os/modconf.c
@@ -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 @@ mod_installfs(struct modlfs *modl, struct modlinkage *modlp)
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 @@ mod_installfs(struct modlfs *modl, struct modlinkage *modlp)
}
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 @@ mod_installfs(struct modlfs *modl, struct modlinkage *modlp)
*/
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 @@ mod_installfs(struct modlfs *modl, struct modlinkage *modlp)
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);
}
diff --git a/usr/src/uts/common/sys/vfs.h b/usr/src/uts/common/sys/vfs.h
index 027be4f504..a8e057da40 100644
--- a/usr/src/uts/common/sys/vfs.h
+++ b/usr/src/uts/common/sys/vfs.h
@@ -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 @@ typedef struct mntopts {
} 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 @@ typedef struct vfs {
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 @@ typedef struct vfs {
#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 @@ enum vntrans {
typedef enum vntrans vntrans_t;
-
-
/*
* Operations supported on virtual file system.
*/
@@ -374,6 +396,7 @@ int fs_build_vector(void *vector, int *unused_ops,
#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 */
diff --git a/usr/src/uts/common/sys/vnode.h b/usr/src/uts/common/sys/vnode.h
index 904d7a80ec..50eaca5fcc 100644
--- a/usr/src/uts/common/sys/vnode.h
+++ b/usr/src/uts/common/sys/vnode.h
@@ -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 @@ typedef struct fs_operation_trans_def {
/*
+ * 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.