summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Telka <marcel.telka@nexenta.com>2015-09-02 08:18:08 +0200
committerDan McDonald <danmcd@omniti.com>2015-09-03 13:57:14 -0400
commit22146ea93e24c7deb02c49c33b2ab98605ce78b4 (patch)
tree549b1d02a3e956d62188c4e7fa5ae855ccf8f4ce
parentcf98b944cdc2063fc14f3fd525e284de3ed29fd0 (diff)
downloadillumos-joyent-22146ea93e24c7deb02c49c33b2ab98605ce78b4.tar.gz
6090 IOPS, bandwidth, and latency kstats for NFS server
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com> Reviewed by: Richard Elling <Richard.Elling@RichardElling.com> Approved by: Dan McDonald <danmcd@omniti.com>
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_dispatch.c21
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_srv.c244
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_srv_ns.c14
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_xdr.c41
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_export.c120
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_server.c95
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_stats.c378
-rw-r--r--usr/src/uts/common/nfs/export.h9
-rw-r--r--usr/src/uts/common/nfs/nfs.h34
-rw-r--r--usr/src/uts/common/nfs/nfs4.h3
-rw-r--r--usr/src/uts/common/nfs/nfs4_kprot.h9
-rw-r--r--usr/src/uts/common/nfs/nfs_acl.h10
-rw-r--r--usr/src/uts/common/nfs/nfs_dispatch.h8
13 files changed, 878 insertions, 108 deletions
diff --git a/usr/src/uts/common/fs/nfs/nfs4_dispatch.c b/usr/src/uts/common/fs/nfs/nfs4_dispatch.c
index fbff936e09..c0a694b219 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_dispatch.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_dispatch.c
@@ -20,6 +20,10 @@
*/
/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -360,6 +364,7 @@ rfs4_find_dr(struct svc_req *req, rfs4_drc_t *drc, rfs4_dupreq_t **dup)
* req The request to process
* xprt The server transport handle
* ap A pointer to the arguments
+ * rlen A pointer to the reply length (output)
*
*
* When appropriate this function is responsible for inserting
@@ -374,7 +379,7 @@ rfs4_find_dr(struct svc_req *req, rfs4_drc_t *drc, rfs4_dupreq_t **dup)
*/
int
rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req,
- SVCXPRT *xprt, char *ap)
+ SVCXPRT *xprt, char *ap, size_t *rlen)
{
COMPOUND4res res_buf;
@@ -400,6 +405,7 @@ rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req,
return (1);
}
DTRACE_NFSV4_1(null__done, struct svc_req *, req);
+ *rlen = xdr_sizeof(xdr_void, NULL);
return (0);
}
@@ -409,6 +415,11 @@ rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req,
cap = (COMPOUND4args *)ap;
/*
+ * Update kstats
+ */
+ rfs4_compound_kstat_args(cap);
+
+ /*
* Figure out the disposition of the whole COMPOUND
* and record it's IDEMPOTENTCY.
*/
@@ -492,12 +503,18 @@ rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req,
/*
* Send out the replayed reply or the 'real' one.
*/
- if (!svc_sendreply(xprt, xdr_COMPOUND4res_srv, (char *)rbp)) {
+ if (!svc_sendreply(xprt, xdr_COMPOUND4res_srv, (char *)rbp)) {
DTRACE_PROBE2(nfss__e__dispatch_sendfail,
struct svc_req *, xprt,
char *, rbp);
svcerr_systemerr(xprt);
error++;
+ } else {
+ /*
+ * Update kstats
+ */
+ rfs4_compound_kstat_res(rbp);
+ *rlen = xdr_sizeof(xdr_COMPOUND4res_srv, rbp);
}
/*
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv.c b/usr/src/uts/common/fs/nfs/nfs4_srv.c
index 2f8f776b02..544c884ad8 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c
@@ -57,6 +57,7 @@
#include <sys/sdt.h>
#include <sys/ddi.h>
#include <sys/zone.h>
+#include <sys/kstat.h>
#include <fs/fs_reparse.h>
@@ -283,132 +284,166 @@ struct rfsv4disp {
void (*dis_proc)(); /* proc to call */
void (*dis_resfree)(); /* frees space allocated by proc */
int dis_flags; /* RPC_IDEMPOTENT, etc... */
+ int op_type; /* operation type, see below */
};
+/*
+ * operation types; used primarily for the per-exportinfo kstat implementation
+ */
+#define NFS4_OP_NOFH 0 /* The operation does not operate with any */
+ /* particular filehandle; we cannot associate */
+ /* it with any exportinfo. */
+
+#define NFS4_OP_CFH 1 /* The operation works with the current */
+ /* filehandle; we associate the operation */
+ /* with the exportinfo related to the current */
+ /* filehandle (as set before the operation is */
+ /* executed). */
+
+#define NFS4_OP_SFH 2 /* The operation works with the saved */
+ /* filehandle; we associate the operation */
+ /* with the exportinfo related to the saved */
+ /* filehandle (as set before the operation is */
+ /* executed). */
+
+#define NFS4_OP_POSTCFH 3 /* The operation ignores the current */
+ /* filehandle, but sets the new current */
+ /* filehandle instead; we associate the */
+ /* operation with the exportinfo related to */
+ /* the current filehandle as set after the */
+ /* operation is successfuly executed. Since */
+ /* we do not know the particular exportinfo */
+ /* (and thus the kstat) before the operation */
+ /* is done, there is no simple way how to */
+ /* update some I/O kstat statistics related */
+ /* to kstat_queue(9F). */
+
static struct rfsv4disp rfsv4disptab[] = {
/*
* NFS VERSION 4
*/
/* RFS_NULL = 0 */
- {rfs4_op_illegal, nullfree, 0},
+ {rfs4_op_illegal, nullfree, 0, NFS4_OP_NOFH},
/* UNUSED = 1 */
- {rfs4_op_illegal, nullfree, 0},
+ {rfs4_op_illegal, nullfree, 0, NFS4_OP_NOFH},
/* UNUSED = 2 */
- {rfs4_op_illegal, nullfree, 0},
+ {rfs4_op_illegal, nullfree, 0, NFS4_OP_NOFH},
/* OP_ACCESS = 3 */
- {rfs4_op_access, nullfree, RPC_IDEMPOTENT},
+ {rfs4_op_access, nullfree, RPC_IDEMPOTENT, NFS4_OP_CFH},
/* OP_CLOSE = 4 */
- {rfs4_op_close, nullfree, 0},
+ {rfs4_op_close, nullfree, 0, NFS4_OP_CFH},
/* OP_COMMIT = 5 */
- {rfs4_op_commit, nullfree, RPC_IDEMPOTENT},
+ {rfs4_op_commit, nullfree, RPC_IDEMPOTENT, NFS4_OP_CFH},
/* OP_CREATE = 6 */
- {rfs4_op_create, nullfree, 0},
+ {rfs4_op_create, nullfree, 0, NFS4_OP_CFH},
/* OP_DELEGPURGE = 7 */
- {rfs4_op_delegpurge, nullfree, 0},
+ {rfs4_op_delegpurge, nullfree, 0, NFS4_OP_NOFH},
/* OP_DELEGRETURN = 8 */
- {rfs4_op_delegreturn, nullfree, 0},
+ {rfs4_op_delegreturn, nullfree, 0, NFS4_OP_CFH},
/* OP_GETATTR = 9 */
- {rfs4_op_getattr, rfs4_op_getattr_free, RPC_IDEMPOTENT},
+ {rfs4_op_getattr, rfs4_op_getattr_free, RPC_IDEMPOTENT, NFS4_OP_CFH},
/* OP_GETFH = 10 */
- {rfs4_op_getfh, rfs4_op_getfh_free, RPC_ALL},
+ {rfs4_op_getfh, rfs4_op_getfh_free, RPC_ALL, NFS4_OP_CFH},
/* OP_LINK = 11 */
- {rfs4_op_link, nullfree, 0},
+ {rfs4_op_link, nullfree, 0, NFS4_OP_CFH},
/* OP_LOCK = 12 */
- {rfs4_op_lock, lock_denied_free, 0},
+ {rfs4_op_lock, lock_denied_free, 0, NFS4_OP_CFH},
/* OP_LOCKT = 13 */
- {rfs4_op_lockt, lock_denied_free, 0},
+ {rfs4_op_lockt, lock_denied_free, 0, NFS4_OP_CFH},
/* OP_LOCKU = 14 */
- {rfs4_op_locku, nullfree, 0},
+ {rfs4_op_locku, nullfree, 0, NFS4_OP_CFH},
/* OP_LOOKUP = 15 */
- {rfs4_op_lookup, nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK)},
+ {rfs4_op_lookup, nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK),
+ NFS4_OP_CFH},
/* OP_LOOKUPP = 16 */
- {rfs4_op_lookupp, nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK)},
+ {rfs4_op_lookupp, nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK),
+ NFS4_OP_CFH},
/* OP_NVERIFY = 17 */
- {rfs4_op_nverify, nullfree, RPC_IDEMPOTENT},
+ {rfs4_op_nverify, nullfree, RPC_IDEMPOTENT, NFS4_OP_CFH},
/* OP_OPEN = 18 */
- {rfs4_op_open, rfs4_free_reply, 0},
+ {rfs4_op_open, rfs4_free_reply, 0, NFS4_OP_CFH},
/* OP_OPENATTR = 19 */
- {rfs4_op_openattr, nullfree, 0},
+ {rfs4_op_openattr, nullfree, 0, NFS4_OP_CFH},
/* OP_OPEN_CONFIRM = 20 */
- {rfs4_op_open_confirm, nullfree, 0},
+ {rfs4_op_open_confirm, nullfree, 0, NFS4_OP_CFH},
/* OP_OPEN_DOWNGRADE = 21 */
- {rfs4_op_open_downgrade, nullfree, 0},
+ {rfs4_op_open_downgrade, nullfree, 0, NFS4_OP_CFH},
/* OP_OPEN_PUTFH = 22 */
- {rfs4_op_putfh, nullfree, RPC_ALL},
+ {rfs4_op_putfh, nullfree, RPC_ALL, NFS4_OP_POSTCFH},
/* OP_PUTPUBFH = 23 */
- {rfs4_op_putpubfh, nullfree, RPC_ALL},
+ {rfs4_op_putpubfh, nullfree, RPC_ALL, NFS4_OP_POSTCFH},
/* OP_PUTROOTFH = 24 */
- {rfs4_op_putrootfh, nullfree, RPC_ALL},
+ {rfs4_op_putrootfh, nullfree, RPC_ALL, NFS4_OP_POSTCFH},
/* OP_READ = 25 */
- {rfs4_op_read, rfs4_op_read_free, RPC_IDEMPOTENT},
+ {rfs4_op_read, rfs4_op_read_free, RPC_IDEMPOTENT, NFS4_OP_CFH},
/* OP_READDIR = 26 */
- {rfs4_op_readdir, rfs4_op_readdir_free, RPC_IDEMPOTENT},
+ {rfs4_op_readdir, rfs4_op_readdir_free, RPC_IDEMPOTENT, NFS4_OP_CFH},
/* OP_READLINK = 27 */
- {rfs4_op_readlink, rfs4_op_readlink_free, RPC_IDEMPOTENT},
+ {rfs4_op_readlink, rfs4_op_readlink_free, RPC_IDEMPOTENT, NFS4_OP_CFH},
/* OP_REMOVE = 28 */
- {rfs4_op_remove, nullfree, 0},
+ {rfs4_op_remove, nullfree, 0, NFS4_OP_CFH},
/* OP_RENAME = 29 */
- {rfs4_op_rename, nullfree, 0},
+ {rfs4_op_rename, nullfree, 0, NFS4_OP_CFH},
/* OP_RENEW = 30 */
- {rfs4_op_renew, nullfree, 0},
+ {rfs4_op_renew, nullfree, 0, NFS4_OP_NOFH},
/* OP_RESTOREFH = 31 */
- {rfs4_op_restorefh, nullfree, RPC_ALL},
+ {rfs4_op_restorefh, nullfree, RPC_ALL, NFS4_OP_SFH},
/* OP_SAVEFH = 32 */
- {rfs4_op_savefh, nullfree, RPC_ALL},
+ {rfs4_op_savefh, nullfree, RPC_ALL, NFS4_OP_CFH},
/* OP_SECINFO = 33 */
- {rfs4_op_secinfo, rfs4_op_secinfo_free, 0},
+ {rfs4_op_secinfo, rfs4_op_secinfo_free, 0, NFS4_OP_CFH},
/* OP_SETATTR = 34 */
- {rfs4_op_setattr, nullfree, 0},
+ {rfs4_op_setattr, nullfree, 0, NFS4_OP_CFH},
/* OP_SETCLIENTID = 35 */
- {rfs4_op_setclientid, nullfree, 0},
+ {rfs4_op_setclientid, nullfree, 0, NFS4_OP_NOFH},
/* OP_SETCLIENTID_CONFIRM = 36 */
- {rfs4_op_setclientid_confirm, nullfree, 0},
+ {rfs4_op_setclientid_confirm, nullfree, 0, NFS4_OP_NOFH},
/* OP_VERIFY = 37 */
- {rfs4_op_verify, nullfree, RPC_IDEMPOTENT},
+ {rfs4_op_verify, nullfree, RPC_IDEMPOTENT, NFS4_OP_CFH},
/* OP_WRITE = 38 */
- {rfs4_op_write, nullfree, 0},
+ {rfs4_op_write, nullfree, 0, NFS4_OP_CFH},
/* OP_RELEASE_LOCKOWNER = 39 */
- {rfs4_op_release_lockowner, nullfree, 0},
+ {rfs4_op_release_lockowner, nullfree, 0, NFS4_OP_NOFH},
};
static uint_t rfsv4disp_cnt = sizeof (rfsv4disptab) / sizeof (rfsv4disptab[0]);
@@ -3535,7 +3570,6 @@ rfs4_op_putfh(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
cs->cr = NULL;
}
-
if (args->object.nfs_fh4_len < NFS_FH4_LEN) {
*cs->statusp = resp->status = NFS4ERR_BADHANDLE;
goto out;
@@ -5853,12 +5887,43 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi,
op = (uint_t)resop->resop;
if (op < rfsv4disp_cnt) {
+ kstat_t *ksp = rfsprocio_v4_ptr[op];
+ kstat_t *exi_ksp = NULL;
+
/*
* Count the individual ops here; NULL and COMPOUND
* are counted in common_dispatch()
*/
rfsproccnt_v4_ptr[op].value.ui64++;
+ if (ksp != NULL) {
+ mutex_enter(ksp->ks_lock);
+ kstat_runq_enter(KSTAT_IO_PTR(ksp));
+ mutex_exit(ksp->ks_lock);
+ }
+
+ switch (rfsv4disptab[op].op_type) {
+ case NFS4_OP_CFH:
+ resop->exi = cs.exi;
+ break;
+ case NFS4_OP_SFH:
+ resop->exi = cs.saved_exi;
+ break;
+ default:
+ ASSERT(resop->exi == NULL);
+ break;
+ }
+
+ if (resop->exi != NULL) {
+ exi_ksp = resop->exi->exi_kstats->
+ rfsprocio_v4_ptr[op];
+ if (exi_ksp != NULL) {
+ mutex_enter(exi_ksp->ks_lock);
+ kstat_runq_enter(KSTAT_IO_PTR(exi_ksp));
+ mutex_exit(exi_ksp->ks_lock);
+ }
+ }
+
NFS4_DEBUG(rfs4_debug > 1,
(CE_NOTE, "Executing %s", rfs4_op_string[op]));
(*rfsv4disptab[op].dis_proc)(argop, resop, req, &cs);
@@ -5866,6 +5931,33 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi,
rfs4_op_string[op], *cs.statusp));
if (*cs.statusp != NFS4_OK)
cs.cont = FALSE;
+
+ if (rfsv4disptab[op].op_type == NFS4_OP_POSTCFH &&
+ *cs.statusp == NFS4_OK &&
+ (resop->exi = cs.exi) != NULL) {
+ exi_ksp = resop->exi->exi_kstats->
+ rfsprocio_v4_ptr[op];
+ }
+
+ if (exi_ksp != NULL) {
+ mutex_enter(exi_ksp->ks_lock);
+ KSTAT_IO_PTR(exi_ksp)->nwritten +=
+ argop->opsize;
+ KSTAT_IO_PTR(exi_ksp)->writes++;
+ if (rfsv4disptab[op].op_type != NFS4_OP_POSTCFH)
+ kstat_runq_exit(KSTAT_IO_PTR(exi_ksp));
+ mutex_exit(exi_ksp->ks_lock);
+
+ exi_hold(resop->exi);
+ } else {
+ resop->exi = NULL;
+ }
+
+ if (ksp != NULL) {
+ mutex_enter(ksp->ks_lock);
+ kstat_runq_exit(KSTAT_IO_PTR(ksp));
+ mutex_exit(ksp->ks_lock);
+ }
} else {
/*
* This is effectively dead code since XDR code
@@ -5886,13 +5978,13 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi,
*/
if ((i + 1) < args->array_len && !cs.cont) {
nfs_resop4 *new_res = kmem_alloc(
- (i+1) * sizeof (nfs_resop4), KM_SLEEP);
+ (i + 1) * sizeof (nfs_resop4), KM_SLEEP);
bcopy(resp->array,
- new_res, (i+1) * sizeof (nfs_resop4));
+ new_res, (i + 1) * sizeof (nfs_resop4));
kmem_free(resp->array,
args->array_len * sizeof (nfs_resop4));
- resp->array_len = i + 1;
+ resp->array_len = i + 1;
resp->array = new_res;
}
}
@@ -5975,6 +6067,70 @@ rfs4_compound_flagproc(COMPOUND4args *args, int *flagp)
*flagp = flag;
}
+void
+rfs4_compound_kstat_args(COMPOUND4args *args)
+{
+ int i;
+
+ for (i = 0; i < args->array_len; i++) {
+ uint_t op = (uint_t)args->array[i].argop;
+
+ if (op < rfsv4disp_cnt) {
+ kstat_t *ksp = rfsprocio_v4_ptr[op];
+
+ if (ksp != NULL) {
+ mutex_enter(ksp->ks_lock);
+ KSTAT_IO_PTR(ksp)->nwritten +=
+ args->array[i].opsize;
+ KSTAT_IO_PTR(ksp)->writes++;
+ mutex_exit(ksp->ks_lock);
+ }
+ }
+ }
+}
+
+void
+rfs4_compound_kstat_res(COMPOUND4res *res)
+{
+ int i;
+
+ for (i = 0; i < res->array_len; i++) {
+ uint_t op = (uint_t)res->array[i].resop;
+
+ if (op < rfsv4disp_cnt) {
+ kstat_t *ksp = rfsprocio_v4_ptr[op];
+ struct exportinfo *exi = res->array[i].exi;
+
+ if (ksp != NULL) {
+ mutex_enter(ksp->ks_lock);
+ KSTAT_IO_PTR(ksp)->nread +=
+ res->array[i].opsize;
+ KSTAT_IO_PTR(ksp)->reads++;
+ mutex_exit(ksp->ks_lock);
+ }
+
+ if (exi != NULL) {
+ kstat_t *exi_ksp;
+
+ rw_enter(&exported_lock, RW_READER);
+
+ exi_ksp = exi->exi_kstats->rfsprocio_v4_ptr[op];
+ if (exi_ksp != NULL) {
+ mutex_enter(exi_ksp->ks_lock);
+ KSTAT_IO_PTR(exi_ksp)->nread +=
+ res->array[i].opsize;
+ KSTAT_IO_PTR(exi_ksp)->reads++;
+ mutex_exit(exi_ksp->ks_lock);
+ }
+
+ rw_exit(&exported_lock);
+
+ exi_rele(exi);
+ }
+ }
+ }
+}
+
nfsstat4
rfs4_client_sysid(rfs4_client_t *cp, sysid_t *sp)
{
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
index ce0c9485a6..bc19d5a116 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -206,6 +206,14 @@ pseudo_exportfs(vnode_t *vp, fid_t *fid, struct exp_visible *vis_head,
*/
export_link(exi);
+ /*
+ * Initialize exi_id and exi_kstats
+ */
+ exi->exi_id = exi_id_get_next();
+ avl_add(&exi_id_tree, exi);
+ exi->exi_kstats = exp_kstats_init(getzoneid(), exi->exi_id,
+ kex->ex_path, vpathlen, TRUE);
+
return (exi);
}
@@ -771,6 +779,8 @@ treeclimb_export(struct exportinfo *exip)
exportinfo_t *e = tree_head->tree_exi;
/* exip will be freed in exportfs() */
if (e && e != exip) {
+ exp_kstats_delete(e->exi_kstats);
+ avl_remove(&exi_id_tree, e);
export_unlink(e);
exi_rele(e);
}
@@ -821,6 +831,8 @@ treeclimb_unexport(struct exportinfo *exip)
/* Release pseudo export if it has no child */
if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
tnode->tree_child_first == 0) {
+ exp_kstats_delete(tnode->tree_exi->exi_kstats);
+ avl_remove(&exi_id_tree, tnode->tree_exi);
export_unlink(tnode->tree_exi);
exi_rele(tnode->tree_exi);
}
diff --git a/usr/src/uts/common/fs/nfs/nfs4_xdr.c b/usr/src/uts/common/fs/nfs/nfs4_xdr.c
index 572e0aa0df..530a3c7746 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_xdr.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_xdr.c
@@ -4316,16 +4316,29 @@ xdr_cnfs_argop4_wrap(XDR *xdrs, nfs_argop4 *objp)
static bool_t
xdr_snfs_argop4(XDR *xdrs, nfs_argop4 *objp)
{
+ uint_t pos;
+ bool_t ret;
+
+ if (xdrs->x_op == XDR_DECODE)
+ pos = XDR_GETPOS(xdrs);
+
if (!xdr_int(xdrs, (int *)&objp->argop))
return (FALSE);
switch (objp->argop) {
case OP_PUTFH:
- return (xdr_decode_nfs_fh4(xdrs,
- &objp->nfs_argop4_u.opputfh.object));
+ ret = xdr_decode_nfs_fh4(xdrs,
+ &objp->nfs_argop4_u.opputfh.object);
+ break;
default:
- return (xdr_nfs_argop4(xdrs, objp));
+ ret = xdr_nfs_argop4(xdrs, objp);
+ break;
}
+
+ if (ret && xdrs->x_op == XDR_DECODE)
+ objp->opsize = XDR_GETPOS(xdrs) - pos;
+
+ return (ret);
}
/*
@@ -4777,6 +4790,12 @@ xdr_nfs_resop4(XDR *xdrs, nfs_resop4 *objp)
static bool_t
xdr_snfs_resop4(XDR *xdrs, nfs_resop4 *objp)
{
+ uint_t pos;
+ bool_t ret;
+
+ if (xdrs->x_op == XDR_ENCODE)
+ pos = XDR_GETPOS(xdrs);
+
if (!xdr_int(xdrs, (int *)&objp->resop))
return (FALSE);
@@ -4786,12 +4805,20 @@ xdr_snfs_resop4(XDR *xdrs, nfs_resop4 *objp)
(int32_t *)&objp->nfs_resop4_u.opgetfh.status))
return (FALSE);
if (objp->nfs_resop4_u.opgetfh.status != NFS4_OK)
- return (TRUE);
- return (xdr_encode_nfs_fh4(xdrs,
- &objp->nfs_resop4_u.opgetfh.object));
+ ret = TRUE;
+ else
+ ret = xdr_encode_nfs_fh4(xdrs,
+ &objp->nfs_resop4_u.opgetfh.object);
+ break;
default:
- return (xdr_nfs_resop4(xdrs, objp));
+ ret = xdr_nfs_resop4(xdrs, objp);
+ break;
}
+
+ if (ret && xdrs->x_op == XDR_ENCODE)
+ objp->opsize = XDR_GETPOS(xdrs) - pos;
+
+ return (ret);
}
static bool_t
diff --git a/usr/src/uts/common/fs/nfs/nfs_export.c b/usr/src/uts/common/fs/nfs/nfs_export.c
index 4c316a3876..c198a2bf6d 100644
--- a/usr/src/uts/common/fs/nfs/nfs_export.c
+++ b/usr/src/uts/common/fs/nfs/nfs_export.c
@@ -72,6 +72,21 @@ treenode_t *ns_root;
struct exportinfo *exptable_path_hash[PKP_HASH_SIZE];
struct exportinfo *exptable[EXPTABLESIZE];
+/*
+ * exi_id support
+ *
+ * exi_id_next The next exi_id available.
+ * exi_id_overflow The exi_id_next already overflowed, so we should
+ * thoroughly check for duplicates.
+ * exi_id_tree AVL tree indexed by exi_id.
+ *
+ * All exi_id_next, exi_id_overflow, and exi_id_tree are protected by
+ * exported_lock.
+ */
+static int exi_id_next;
+static bool_t exi_id_overflow;
+avl_tree_t exi_id_tree;
+
static int unexport(exportinfo_t *);
static void exportfree(exportinfo_t *);
static int loadindex(exportdata_t *);
@@ -785,6 +800,8 @@ export_link(exportinfo_t *exi)
{
exportinfo_t **bckt;
+ ASSERT(RW_WRITE_HELD(&exported_lock));
+
bckt = &exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
exp_hash_link(exi, fid_hash, bckt);
@@ -794,6 +811,48 @@ export_link(exportinfo_t *exi)
}
/*
+ * Helper functions for exi_id handling
+ */
+static int
+exi_id_compar(const void *v1, const void *v2)
+{
+ const struct exportinfo *e1 = v1;
+ const struct exportinfo *e2 = v2;
+
+ if (e1->exi_id < e2->exi_id)
+ return (-1);
+ if (e1->exi_id > e2->exi_id)
+ return (1);
+
+ return (0);
+}
+
+int
+exi_id_get_next(void)
+{
+ struct exportinfo e;
+ int ret = exi_id_next;
+
+ ASSERT(RW_WRITE_HELD(&exported_lock));
+
+ do {
+ exi_id_next++;
+ if (exi_id_next == 0)
+ exi_id_overflow = TRUE;
+
+ if (!exi_id_overflow)
+ break;
+
+ if (exi_id_next == ret)
+ cmn_err(CE_PANIC, "exi_id exhausted");
+
+ e.exi_id = exi_id_next;
+ } while (avl_find(&exi_id_tree, &e, NULL) != NULL);
+
+ return (ret);
+}
+
+/*
* Initialization routine for export routines. Should only be called once.
*/
int
@@ -805,6 +864,14 @@ nfs_exportinit(void)
rw_init(&exported_lock, NULL, RW_DEFAULT, NULL);
/*
+ * exi_id handling initialization
+ */
+ exi_id_next = 0;
+ exi_id_overflow = FALSE;
+ avl_create(&exi_id_tree, exi_id_compar, sizeof (struct exportinfo),
+ offsetof(struct exportinfo, exi_id_link));
+
+ /*
* Allocate the place holder for the public file handle, which
* is all zeroes. It is initially set to the root filesystem.
*/
@@ -849,11 +916,24 @@ nfs_exportinit(void)
exi_rootfid.fid_len);
exi_root->exi_fh.fh_len = sizeof (exi_root->exi_fh.fh_data);
+ rw_enter(&exported_lock, RW_WRITER);
+
/*
* Publish the exportinfo in the hash table
*/
export_link(exi_root);
+ /*
+ * Initialize exi_id and exi_kstats
+ */
+ exi_root->exi_id = exi_id_get_next();
+ avl_add(&exi_id_tree, exi_root);
+ exi_root->exi_kstats = exp_kstats_init(getzoneid(), exi_root->exi_id,
+ exi_root->exi_export.ex_path, exi_root->exi_export.ex_pathlen,
+ FALSE);
+
+ rw_exit(&exported_lock);
+
nfslog_init();
ns_root = NULL;
@@ -869,19 +949,36 @@ nfs_exportfini(void)
{
int i;
+ rw_enter(&exported_lock, RW_WRITER);
+
+ exp_kstats_delete(exi_root->exi_kstats);
+ avl_remove(&exi_id_tree, exi_root);
+ export_unlink(exi_root);
+
+ rw_exit(&exported_lock);
+
/*
* Deallocate the place holder for the public file handle.
*/
srv_secinfo_list_free(exi_root->exi_export.ex_secinfo,
exi_root->exi_export.ex_seccnt);
mutex_destroy(&exi_root->exi_lock);
+
rw_destroy(&exi_root->exi_cache_lock);
for (i = 0; i < AUTH_TABLESIZE; i++) {
avl_destroy(exi_root->exi_cache[i]);
kmem_free(exi_root->exi_cache[i], sizeof (avl_tree_t));
}
+
+ exp_kstats_fini(exi_root->exi_kstats);
+
kmem_free(exi_root, sizeof (*exi_root));
+ /*
+ * exi_id handling cleanup
+ */
+ avl_destroy(&exi_id_tree);
+
rw_destroy(&exported_lock);
}
@@ -1472,6 +1569,7 @@ exportfs(struct exportfs_args *args, model_t model, cred_t *cr)
*/
for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) {
if (ex != exi_root && VN_CMP(ex->exi_vp, vp)) {
+ avl_remove(&exi_id_tree, ex);
export_unlink(ex);
break;
}
@@ -1517,7 +1615,7 @@ exportfs(struct exportfs_args *args, model_t model, cred_t *cr)
if (error)
goto out7;
} else {
- /* If it's a re-export update namespace tree */
+ /* If it's a re-export update namespace tree */
exi->exi_tree = ex->exi_tree;
exi->exi_tree->tree_exi = exi;
}
@@ -1567,6 +1665,22 @@ exportfs(struct exportfs_args *args, model_t model, cred_t *cr)
ex->exi_visible = NULL;
}
+ /*
+ * Initialize exi_id and exi_kstats
+ */
+ if (ex != NULL) {
+ exi->exi_id = ex->exi_id;
+ exi->exi_kstats = ex->exi_kstats;
+ ex->exi_kstats = NULL;
+ exp_kstats_reset(exi->exi_kstats, kex->ex_path,
+ kex->ex_pathlen, FALSE);
+ } else {
+ exi->exi_id = exi_id_get_next();
+ exi->exi_kstats = exp_kstats_init(getzoneid(), exi->exi_id,
+ kex->ex_path, kex->ex_pathlen, FALSE);
+ }
+ avl_add(&exi_id_tree, exi);
+
DTRACE_PROBE(nfss__i__exported_lock3_stop);
rw_exit(&exported_lock);
@@ -1655,6 +1769,8 @@ unexport(struct exportinfo *exi)
return (EINVAL);
}
+ exp_kstats_delete(exi->exi_kstats);
+ avl_remove(&exi_id_tree, exi);
export_unlink(exi);
/*
@@ -2576,6 +2692,8 @@ exportfree(struct exportinfo *exi)
kmem_free(exi->exi_cache[i], sizeof (avl_tree_t));
}
+ exp_kstats_fini(exi->exi_kstats);
+
kmem_free(exi, sizeof (*exi));
}
diff --git a/usr/src/uts/common/fs/nfs/nfs_server.c b/usr/src/uts/common/fs/nfs/nfs_server.c
index 7e94c62734..be28ac9071 100644
--- a/usr/src/uts/common/fs/nfs/nfs_server.c
+++ b/usr/src/uts/common/fs/nfs/nfs_server.c
@@ -18,11 +18,12 @@
*
* CDDL HEADER END
*/
+
/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Bayard G. Bell. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -271,7 +272,8 @@ static kcondvar_t nfs_server_upordown_cv;
*/
nvlist_t *rfs4_dss_paths, *rfs4_dss_oldpaths;
-int rfs4_dispatch(struct rpcdisp *, struct svc_req *, SVCXPRT *, char *);
+int rfs4_dispatch(struct rpcdisp *, struct svc_req *, SVCXPRT *, char *,
+ size_t *);
bool_t rfs4_minorvers_mismatch(struct svc_req *, SVCXPRT *, void *);
/*
@@ -1340,13 +1342,13 @@ union rfs_res {
static struct rpc_disptable rfs_disptable[] = {
{sizeof (rfsdisptab_v2) / sizeof (rfsdisptab_v2[0]),
rfscallnames_v2,
- &rfsproccnt_v2_ptr, rfsdisptab_v2},
+ &rfsproccnt_v2_ptr, &rfsprocio_v2_ptr, rfsdisptab_v2},
{sizeof (rfsdisptab_v3) / sizeof (rfsdisptab_v3[0]),
rfscallnames_v3,
- &rfsproccnt_v3_ptr, rfsdisptab_v3},
+ &rfsproccnt_v3_ptr, &rfsprocio_v3_ptr, rfsdisptab_v3},
{sizeof (rfsdisptab_v4) / sizeof (rfsdisptab_v4[0]),
rfscallnames_v4,
- &rfsproccnt_v4_ptr, rfsdisptab_v4},
+ &rfsproccnt_v4_ptr, &rfsprocio_v4_ptr, rfsdisptab_v4},
};
/*
@@ -1506,6 +1508,11 @@ common_dispatch(struct svc_req *req, SVCXPRT *xprt, rpcvers_t min_vers,
char **procnames;
char cbuf[INET6_ADDRSTRLEN]; /* to hold both IPv4 and IPv6 addr */
bool_t ro = FALSE;
+ kstat_t *ksp = NULL;
+ kstat_t *exi_ksp = NULL;
+ size_t pos; /* request size */
+ size_t rlen; /* reply size */
+ bool_t rsent = FALSE; /* reply was sent successfully */
vers = req->rq_vers;
@@ -1526,6 +1533,14 @@ common_dispatch(struct svc_req *req, SVCXPRT *xprt, rpcvers_t min_vers,
(*(disptable[(int)vers].dis_proccntp))[which].value.ui64++;
+ ksp = (*(disptable[(int)vers].dis_prociop))[which];
+ if (ksp != NULL) {
+ mutex_enter(ksp->ks_lock);
+ kstat_runq_enter(KSTAT_IO_PTR(ksp));
+ mutex_exit(ksp->ks_lock);
+ }
+ pos = XDR_GETPOS(&xprt->xp_xdrin);
+
disp = &disptable[(int)vers].dis_table[which];
procnames = disptable[(int)vers].dis_procnames;
@@ -1569,7 +1584,9 @@ common_dispatch(struct svc_req *req, SVCXPRT *xprt, rpcvers_t min_vers,
* If Version 4 use that specific dispatch function.
*/
if (req->rq_vers == 4) {
- error += rfs4_dispatch(disp, req, xprt, args);
+ error += rfs4_dispatch(disp, req, xprt, args, &rlen);
+ if (error == 0)
+ rsent = TRUE;
goto done;
}
@@ -1648,6 +1665,32 @@ common_dispatch(struct svc_req *req, SVCXPRT *xprt, rpcvers_t min_vers,
exi = checkexport(fsid, xfid);
if (exi != NULL) {
+ rw_enter(&exported_lock, RW_READER);
+
+ switch (req->rq_vers) {
+ case NFS_VERSION:
+ exi_ksp = (disptable == rfs_disptable) ?
+ exi->exi_kstats->rfsprocio_v2_ptr[which] :
+ exi->exi_kstats->aclprocio_v2_ptr[which];
+ break;
+ case NFS_V3:
+ exi_ksp = (disptable == rfs_disptable) ?
+ exi->exi_kstats->rfsprocio_v3_ptr[which] :
+ exi->exi_kstats->aclprocio_v3_ptr[which];
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ if (exi_ksp != NULL) {
+ mutex_enter(exi_ksp->ks_lock);
+ kstat_runq_enter(KSTAT_IO_PTR(exi_ksp));
+ mutex_exit(exi_ksp->ks_lock);
+ } else {
+ rw_exit(&exported_lock);
+ }
+
publicfh_ok = PUBLICFH_CHECK(disp, exi, fsid, xfid);
/*
@@ -1798,12 +1841,18 @@ common_dispatch(struct svc_req *req, SVCXPRT *xprt, rpcvers_t min_vers,
cmn_err(CE_NOTE, "%s: bad sendreply", pgmname);
svcerr_systemerr(xprt);
error++;
+ } else {
+ rlen = xdr_sizeof(disp->dis_fastxdrres, res);
+ rsent = TRUE;
}
} else {
if (!svc_sendreply(xprt, disp->dis_xdrres, res)) {
cmn_err(CE_NOTE, "%s: bad sendreply", pgmname);
svcerr_systemerr(xprt);
error++;
+ } else {
+ rlen = xdr_sizeof(disp->dis_xdrres, res);
+ rsent = TRUE;
}
}
@@ -1826,6 +1875,10 @@ common_dispatch(struct svc_req *req, SVCXPRT *xprt, rpcvers_t min_vers,
}
done:
+ if (ksp != NULL || exi_ksp != NULL) {
+ pos = XDR_GETPOS(&xprt->xp_xdrin) - pos;
+ }
+
/*
* Free arguments struct
*/
@@ -1841,9 +1894,35 @@ done:
}
}
+ if (exi_ksp != NULL) {
+ mutex_enter(exi_ksp->ks_lock);
+ KSTAT_IO_PTR(exi_ksp)->nwritten += pos;
+ KSTAT_IO_PTR(exi_ksp)->writes++;
+ if (rsent) {
+ KSTAT_IO_PTR(exi_ksp)->nread += rlen;
+ KSTAT_IO_PTR(exi_ksp)->reads++;
+ }
+ kstat_runq_exit(KSTAT_IO_PTR(exi_ksp));
+ mutex_exit(exi_ksp->ks_lock);
+
+ rw_exit(&exported_lock);
+ }
+
if (exi != NULL)
exi_rele(exi);
+ if (ksp != NULL) {
+ mutex_enter(ksp->ks_lock);
+ KSTAT_IO_PTR(ksp)->nwritten += pos;
+ KSTAT_IO_PTR(ksp)->writes++;
+ if (rsent) {
+ KSTAT_IO_PTR(ksp)->nread += rlen;
+ KSTAT_IO_PTR(ksp)->reads++;
+ }
+ kstat_runq_exit(KSTAT_IO_PTR(ksp));
+ mutex_exit(ksp->ks_lock);
+ }
+
global_svstat_ptr[req->rq_vers][NFS_BADCALLS].value.ui64 += error;
global_svstat_ptr[req->rq_vers][NFS_CALLS].value.ui64++;
@@ -1969,10 +2048,10 @@ static struct rpcdisp acldisptab_v3[] = {
static struct rpc_disptable acl_disptable[] = {
{sizeof (acldisptab_v2) / sizeof (acldisptab_v2[0]),
aclcallnames_v2,
- &aclproccnt_v2_ptr, acldisptab_v2},
+ &aclproccnt_v2_ptr, &aclprocio_v2_ptr, acldisptab_v2},
{sizeof (acldisptab_v3) / sizeof (acldisptab_v3[0]),
aclcallnames_v3,
- &aclproccnt_v3_ptr, acldisptab_v3},
+ &aclproccnt_v3_ptr, &aclprocio_v3_ptr, acldisptab_v3},
};
static void
diff --git a/usr/src/uts/common/fs/nfs/nfs_stats.c b/usr/src/uts/common/fs/nfs/nfs_stats.c
index baaf47a82a..03e27e6296 100644
--- a/usr/src/uts/common/fs/nfs/nfs_stats.c
+++ b/usr/src/uts/common/fs/nfs/nfs_stats.c
@@ -18,6 +18,11 @@
*
* CDDL HEADER END
*/
+
+/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -127,6 +132,53 @@ nfsstat_zone_fini_server(zoneid_t zoneid, kstat_named_t **svstatp)
}
/*
+ * Support functions for the kstat_io alloc/free
+ */
+static kstat_t **
+rfs_kstat_io_init(zoneid_t zoneid, const char *module, int instance,
+ const char *name, const char *class, const kstat_named_t *tmpl, int count,
+ kmutex_t *lock)
+{
+ int i;
+ kstat_t **ret = kmem_alloc(count * sizeof (*ret), KM_SLEEP);
+
+ for (i = 0; i < count; i++) {
+ char namebuf[KSTAT_STRLEN];
+
+ (void) snprintf(namebuf, sizeof (namebuf), "%s_%s", name,
+ tmpl[i].name);
+ ret[i] = kstat_create_zone(module, instance, namebuf, class,
+ KSTAT_TYPE_IO, 1, 0, zoneid);
+ if (ret[i] != NULL) {
+ ret[i]->ks_lock = lock;
+ kstat_install(ret[i]);
+ }
+ }
+
+ return (ret);
+}
+
+static void
+rfs_kstat_io_delete(kstat_t **ks, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (ks[i] != NULL) {
+ kstat_delete(ks[i]);
+ ks[i] = NULL;
+ }
+ }
+}
+
+static void
+rfs_kstat_io_free(kstat_t **ks, int count)
+{
+ rfs_kstat_io_delete(ks, count);
+ kmem_free(ks, count * sizeof (*ks));
+}
+
+/*
* NFSv2 client stats
*/
static const kstat_named_t rfsreqcnt_v2_tmpl[] = {
@@ -188,27 +240,44 @@ static const kstat_named_t rfsproccnt_v2_tmpl[] = {
{ "statfs", KSTAT_DATA_UINT64 }
};
+#define RFSPROCCNT_V2_COUNT \
+ (sizeof (rfsproccnt_v2_tmpl) / sizeof (rfsproccnt_v2_tmpl[0]))
+
kstat_named_t *rfsproccnt_v2_ptr;
+kstat_t **rfsprocio_v2_ptr;
static void
nfsstat_zone_init_rfsproc_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- kstat_named_t *ks_data;
+ statsp->rfsproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
+ "rfsproccnt_v2", rfsproccnt_v2_tmpl, sizeof (rfsproccnt_v2_tmpl));
- ks_data = nfsstat_zone_init_common(zoneid, "nfs", 0, "rfsproccnt_v2",
- rfsproccnt_v2_tmpl, sizeof (rfsproccnt_v2_tmpl));
- statsp->rfsproccnt_ptr = ks_data;
- if (zoneid == GLOBAL_ZONEID)
- rfsproccnt_v2_ptr = ks_data;
+ mutex_init(&statsp->rfsprocio_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ statsp->rfsprocio_ptr = rfs_kstat_io_init(zoneid, "nfs", 0,
+ "rfsprocio_v2", "rfsprocio_v2", rfsproccnt_v2_tmpl,
+ RFSPROCCNT_V2_COUNT, &statsp->rfsprocio_lock);
+
+ if (zoneid == GLOBAL_ZONEID) {
+ rfsproccnt_v2_ptr = statsp->rfsproccnt_ptr;
+ rfsprocio_v2_ptr = statsp->rfsprocio_ptr;
+ }
}
static void
nfsstat_zone_fini_rfsproc_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- if (zoneid == GLOBAL_ZONEID)
+ if (zoneid == GLOBAL_ZONEID) {
rfsproccnt_v2_ptr = NULL;
+ rfsprocio_v2_ptr = NULL;
+ }
+
nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsproccnt_v2");
kmem_free(statsp->rfsproccnt_ptr, sizeof (rfsproccnt_v2_tmpl));
+
+ rfs_kstat_io_free(statsp->rfsprocio_ptr, RFSPROCCNT_V2_COUNT);
+
+ mutex_destroy(&statsp->rfsprocio_lock);
}
/*
@@ -249,28 +318,44 @@ static const kstat_named_t aclproccnt_v2_tmpl[] = {
{ "getxattrdir", KSTAT_DATA_UINT64 }
};
+#define ACLPROCCNT_V2_COUNT \
+ (sizeof (aclproccnt_v2_tmpl) / sizeof (aclproccnt_v2_tmpl[0]))
+
kstat_named_t *aclproccnt_v2_ptr;
+kstat_t **aclprocio_v2_ptr;
static void
nfsstat_zone_init_aclproc_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- kstat_named_t *ks_data;
+ statsp->aclproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
+ "aclproccnt_v2", aclproccnt_v2_tmpl, sizeof (aclproccnt_v2_tmpl));
- ks_data = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
- "aclproccnt_v2", aclproccnt_v2_tmpl,
- sizeof (aclproccnt_v2_tmpl));
- statsp->aclproccnt_ptr = ks_data;
- if (zoneid == GLOBAL_ZONEID)
- aclproccnt_v2_ptr = ks_data;
+ mutex_init(&statsp->aclprocio_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ statsp->aclprocio_ptr = rfs_kstat_io_init(zoneid, "nfs_acl", 0,
+ "aclprocio_v2", "aclprocio_v2", aclproccnt_v2_tmpl,
+ ACLPROCCNT_V2_COUNT, &statsp->aclprocio_lock);
+
+ if (zoneid == GLOBAL_ZONEID) {
+ aclproccnt_v2_ptr = statsp->aclproccnt_ptr;
+ aclprocio_v2_ptr = statsp->aclprocio_ptr;
+ }
}
static void
nfsstat_zone_fini_aclproc_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- if (zoneid == GLOBAL_ZONEID)
+ if (zoneid == GLOBAL_ZONEID) {
aclproccnt_v2_ptr = NULL;
+ aclprocio_v2_ptr = NULL;
+ }
+
nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclproccnt_v2");
kmem_free(statsp->aclproccnt_ptr, sizeof (aclproccnt_v2_tmpl));
+
+ rfs_kstat_io_free(statsp->aclprocio_ptr, ACLPROCCNT_V2_COUNT);
+
+ mutex_destroy(&statsp->aclprocio_lock);
}
/*
@@ -343,27 +428,44 @@ static const kstat_named_t rfsproccnt_v3_tmpl[] = {
{ "commit", KSTAT_DATA_UINT64 }
};
+#define RFSPROCCNT_V3_COUNT \
+ (sizeof (rfsproccnt_v3_tmpl) / sizeof (rfsproccnt_v3_tmpl[0]))
+
kstat_named_t *rfsproccnt_v3_ptr;
+kstat_t **rfsprocio_v3_ptr;
static void
nfsstat_zone_init_rfsproc_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- kstat_named_t *ks_data;
+ statsp->rfsproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
+ "rfsproccnt_v3", rfsproccnt_v3_tmpl, sizeof (rfsproccnt_v3_tmpl));
- ks_data = nfsstat_zone_init_common(zoneid, "nfs", 0, "rfsproccnt_v3",
- rfsproccnt_v3_tmpl, sizeof (rfsproccnt_v3_tmpl));
- statsp->rfsproccnt_ptr = ks_data;
- if (zoneid == GLOBAL_ZONEID)
- rfsproccnt_v3_ptr = ks_data;
+ mutex_init(&statsp->rfsprocio_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ statsp->rfsprocio_ptr = rfs_kstat_io_init(zoneid, "nfs", 0,
+ "rfsprocio_v3", "rfsprocio_v3", rfsproccnt_v3_tmpl,
+ RFSPROCCNT_V3_COUNT, &statsp->rfsprocio_lock);
+
+ if (zoneid == GLOBAL_ZONEID) {
+ rfsproccnt_v3_ptr = statsp->rfsproccnt_ptr;
+ rfsprocio_v3_ptr = statsp->rfsprocio_ptr;
+ }
}
static void
nfsstat_zone_fini_rfsproc_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- if (zoneid == GLOBAL_ZONEID)
+ if (zoneid == GLOBAL_ZONEID) {
rfsproccnt_v3_ptr = NULL;
+ rfsprocio_v3_ptr = NULL;
+ }
+
nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsproccnt_v3");
kmem_free(statsp->rfsproccnt_ptr, sizeof (rfsproccnt_v3_tmpl));
+
+ rfs_kstat_io_free(statsp->rfsprocio_ptr, RFSPROCCNT_V3_COUNT);
+
+ mutex_destroy(&statsp->rfsprocio_lock);
}
/*
@@ -400,28 +502,44 @@ static const kstat_named_t aclproccnt_v3_tmpl[] = {
{ "getxattrdir", KSTAT_DATA_UINT64 }
};
+#define ACLPROCCNT_V3_COUNT \
+ (sizeof (aclproccnt_v3_tmpl) / sizeof (aclproccnt_v3_tmpl[0]))
+
kstat_named_t *aclproccnt_v3_ptr;
+kstat_t **aclprocio_v3_ptr;
static void
nfsstat_zone_init_aclproc_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- kstat_named_t *ks_data;
+ statsp->aclproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
+ "aclproccnt_v3", aclproccnt_v3_tmpl, sizeof (aclproccnt_v3_tmpl));
- ks_data = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
- "aclproccnt_v3", aclproccnt_v3_tmpl,
- sizeof (aclproccnt_v3_tmpl));
- statsp->aclproccnt_ptr = ks_data;
- if (zoneid == GLOBAL_ZONEID)
- aclproccnt_v3_ptr = ks_data;
+ mutex_init(&statsp->aclprocio_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ statsp->aclprocio_ptr = rfs_kstat_io_init(zoneid, "nfs_acl", 0,
+ "aclprocio_v3", "aclprocio_v3", aclproccnt_v3_tmpl,
+ ACLPROCCNT_V3_COUNT, &statsp->aclprocio_lock);
+
+ if (zoneid == GLOBAL_ZONEID) {
+ aclproccnt_v3_ptr = statsp->aclproccnt_ptr;
+ aclprocio_v3_ptr = statsp->aclprocio_ptr;
+ }
}
static void
nfsstat_zone_fini_aclproc_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- if (zoneid == GLOBAL_ZONEID)
+ if (zoneid == GLOBAL_ZONEID) {
aclproccnt_v3_ptr = NULL;
+ aclprocio_v3_ptr = NULL;
+ }
+
nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclproccnt_v3");
kmem_free(statsp->aclproccnt_ptr, sizeof (aclproccnt_v3_tmpl));
+
+ rfs_kstat_io_free(statsp->aclprocio_ptr, ACLPROCCNT_V3_COUNT);
+
+ mutex_destroy(&statsp->aclprocio_lock);
}
/*
@@ -530,27 +648,44 @@ static const kstat_named_t rfsproccnt_v4_tmpl[] = {
{ "illegal", KSTAT_DATA_UINT64 },
};
+#define RFSPROCCNT_V4_COUNT \
+ (sizeof (rfsproccnt_v4_tmpl) / sizeof (rfsproccnt_v4_tmpl[0]))
+
kstat_named_t *rfsproccnt_v4_ptr;
+kstat_t **rfsprocio_v4_ptr;
static void
nfsstat_zone_init_rfsproc_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- kstat_named_t *ks_data;
+ statsp->rfsproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
+ "rfsproccnt_v4", rfsproccnt_v4_tmpl, sizeof (rfsproccnt_v4_tmpl));
- ks_data = nfsstat_zone_init_common(zoneid, "nfs", 0, "rfsproccnt_v4",
- rfsproccnt_v4_tmpl, sizeof (rfsproccnt_v4_tmpl));
- statsp->rfsproccnt_ptr = ks_data;
- if (zoneid == GLOBAL_ZONEID)
- rfsproccnt_v4_ptr = ks_data;
+ mutex_init(&statsp->rfsprocio_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ statsp->rfsprocio_ptr = rfs_kstat_io_init(zoneid, "nfs", 0,
+ "rfsprocio_v4", "rfsprocio_v4", rfsproccnt_v4_tmpl,
+ RFSPROCCNT_V4_COUNT, &statsp->rfsprocio_lock);
+
+ if (zoneid == GLOBAL_ZONEID) {
+ rfsproccnt_v4_ptr = statsp->rfsproccnt_ptr;
+ rfsprocio_v4_ptr = statsp->rfsprocio_ptr;
+ }
}
static void
nfsstat_zone_fini_rfsproc_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
{
- if (zoneid == GLOBAL_ZONEID)
+ if (zoneid == GLOBAL_ZONEID) {
rfsproccnt_v4_ptr = NULL;
+ rfsprocio_v4_ptr = NULL;
+ }
+
nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsproccnt_v4");
kmem_free(statsp->rfsproccnt_ptr, sizeof (rfsproccnt_v4_tmpl));
+
+ rfs_kstat_io_free(statsp->rfsprocio_ptr, RFSPROCCNT_V4_COUNT);
+
+ mutex_destroy(&statsp->rfsprocio_lock);
}
/*
@@ -686,3 +821,172 @@ nfsstat_zone_fini(zoneid_t zoneid, void *data)
kmem_free(nfs_stats_ptr, sizeof (*nfs_stats_ptr));
}
+
+/*
+ * Support for exp_kstats initialization and tear down
+ */
+struct exp_kstats *
+exp_kstats_init(zoneid_t zoneid, int instance, const char *path, size_t len,
+ bool_t pseudo)
+{
+ struct exp_kstats *exp_kstats;
+
+ exp_kstats = kmem_alloc(sizeof (*exp_kstats), KM_SLEEP);
+
+ mutex_init(&exp_kstats->procio_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /*
+ * Generic share kstat.
+ */
+ exp_kstats->share_kstat = kstat_create_zone("nfs", instance, "share",
+ "misc", KSTAT_TYPE_NAMED,
+ sizeof (exp_kstats->share_kstat_data) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_VAR_SIZE, zoneid);
+ if (exp_kstats->share_kstat != NULL) {
+ len = strnlen(path, len);
+ exp_kstats->share_path = kmem_alloc(len + 1, KM_SLEEP);
+ bcopy(path, exp_kstats->share_path, len);
+ exp_kstats->share_path[len] = '\0';
+
+ exp_kstats->share_kstat->ks_data =
+ &exp_kstats->share_kstat_data;
+
+ kstat_named_init(&exp_kstats->share_kstat_data.path, "path",
+ KSTAT_DATA_STRING);
+ kstat_named_setstr(&exp_kstats->share_kstat_data.path,
+ exp_kstats->share_path);
+
+ kstat_named_init(&exp_kstats->share_kstat_data.filesystem,
+ "filesystem", KSTAT_DATA_STRING);
+ kstat_named_setstr(&exp_kstats->share_kstat_data.filesystem,
+ pseudo ? "pseudo" : "real");
+
+ exp_kstats->share_kstat->ks_lock = &exp_kstats->procio_lock;
+ kstat_install(exp_kstats->share_kstat);
+ }
+
+ /*
+ * NFS_ACL version 2
+ */
+ exp_kstats->aclprocio_v2_ptr = rfs_kstat_io_init(zoneid, "nfs_acl",
+ instance, "share_v2", "aclprocio_v2", aclproccnt_v2_tmpl,
+ ACLPROCCNT_V2_COUNT, &exp_kstats->procio_lock);
+
+ /*
+ * NFS_ACL version 3
+ */
+ exp_kstats->aclprocio_v3_ptr = rfs_kstat_io_init(zoneid, "nfs_acl",
+ instance, "share_v3", "aclprocio_v3", aclproccnt_v3_tmpl,
+ ACLPROCCNT_V3_COUNT, &exp_kstats->procio_lock);
+
+ /*
+ * NFS version 2
+ */
+ exp_kstats->rfsprocio_v2_ptr = rfs_kstat_io_init(zoneid, "nfs",
+ instance, "share_v2", "rfsprocio_v2", rfsproccnt_v2_tmpl,
+ RFSPROCCNT_V2_COUNT, &exp_kstats->procio_lock);
+
+ /*
+ * NFS version 3
+ */
+ exp_kstats->rfsprocio_v3_ptr = rfs_kstat_io_init(zoneid, "nfs",
+ instance, "share_v3", "rfsprocio_v3", rfsproccnt_v3_tmpl,
+ RFSPROCCNT_V3_COUNT, &exp_kstats->procio_lock);
+
+ /*
+ * NFS version 4
+ */
+ exp_kstats->rfsprocio_v4_ptr = rfs_kstat_io_init(zoneid, "nfs",
+ instance, "share_v4", "rfsprocio_v4", rfsproccnt_v4_tmpl,
+ RFSPROCCNT_V4_COUNT, &exp_kstats->procio_lock);
+
+ return (exp_kstats);
+}
+
+void
+exp_kstats_delete(struct exp_kstats *exp_kstats)
+{
+ if (exp_kstats == NULL)
+ return;
+
+ /*
+ * Generic share kstat
+ */
+ if (exp_kstats->share_kstat != NULL) {
+ kstat_delete(exp_kstats->share_kstat);
+ exp_kstats->share_kstat = NULL;
+ strfree(exp_kstats->share_path);
+ }
+
+ /*
+ * NFS_ACL kstats
+ */
+ rfs_kstat_io_delete(exp_kstats->aclprocio_v2_ptr, ACLPROCCNT_V2_COUNT);
+ rfs_kstat_io_delete(exp_kstats->aclprocio_v3_ptr, ACLPROCCNT_V3_COUNT);
+
+ /*
+ * NFS kstats
+ */
+ rfs_kstat_io_delete(exp_kstats->rfsprocio_v2_ptr, RFSPROCCNT_V2_COUNT);
+ rfs_kstat_io_delete(exp_kstats->rfsprocio_v3_ptr, RFSPROCCNT_V3_COUNT);
+ rfs_kstat_io_delete(exp_kstats->rfsprocio_v4_ptr, RFSPROCCNT_V4_COUNT);
+}
+
+void
+exp_kstats_fini(struct exp_kstats *exp_kstats)
+{
+ if (exp_kstats == NULL)
+ return;
+
+ /*
+ * Generic share kstat
+ */
+ if (exp_kstats->share_kstat != NULL) {
+ kstat_delete(exp_kstats->share_kstat);
+ strfree(exp_kstats->share_path);
+ }
+
+ /*
+ * NFS_ACL kstats
+ */
+ rfs_kstat_io_free(exp_kstats->aclprocio_v2_ptr, ACLPROCCNT_V2_COUNT);
+ rfs_kstat_io_free(exp_kstats->aclprocio_v3_ptr, ACLPROCCNT_V3_COUNT);
+
+ /*
+ * NFS kstats
+ */
+ rfs_kstat_io_free(exp_kstats->rfsprocio_v2_ptr, RFSPROCCNT_V2_COUNT);
+ rfs_kstat_io_free(exp_kstats->rfsprocio_v3_ptr, RFSPROCCNT_V3_COUNT);
+ rfs_kstat_io_free(exp_kstats->rfsprocio_v4_ptr, RFSPROCCNT_V4_COUNT);
+
+ mutex_destroy(&exp_kstats->procio_lock);
+
+ kmem_free(exp_kstats, sizeof (*exp_kstats));
+}
+
+void
+exp_kstats_reset(struct exp_kstats *exp_kstats, const char *path, size_t len,
+ bool_t pseudo)
+{
+ char *old;
+ char *new;
+
+ if (exp_kstats->share_kstat == NULL)
+ return;
+
+ len = strnlen(path, len);
+ new = kmem_alloc(len + 1, KM_SLEEP);
+ bcopy(path, new, len);
+ new[len] = '\0';
+
+ mutex_enter(exp_kstats->share_kstat->ks_lock);
+ old = exp_kstats->share_path;
+ exp_kstats->share_path = new;
+ kstat_named_setstr(&exp_kstats->share_kstat_data.path,
+ exp_kstats->share_path);
+ kstat_named_setstr(&exp_kstats->share_kstat_data.filesystem,
+ pseudo ? "pseudo" : "real");
+ mutex_exit(exp_kstats->share_kstat->ks_lock);
+
+ strfree(old);
+}
diff --git a/usr/src/uts/common/nfs/export.h b/usr/src/uts/common/nfs/export.h
index 982bc9d448..2ed9aa30d6 100644
--- a/usr/src/uts/common/nfs/export.h
+++ b/usr/src/uts/common/nfs/export.h
@@ -530,6 +530,9 @@ struct exportinfo {
struct ex_vol_rename *exi_vol_rename;
kmutex_t exi_vol_rename_lock;
#endif /* VOLATILE_FH_TEST */
+ int exi_id;
+ avl_node_t exi_id_link;
+ struct exp_kstats *exi_kstats;
};
typedef struct exportinfo exportinfo_t;
@@ -662,6 +665,12 @@ extern krwlock_t exported_lock;
extern struct exportinfo *exptable[];
/*
+ * exi_id support
+ */
+extern avl_tree_t exi_id_tree;
+extern int exi_id_get_next(void);
+
+/*
* Two macros for identifying public filehandles.
* A v2 public filehandle is 32 zero bytes.
* A v3 public filehandle is zero length.
diff --git a/usr/src/uts/common/nfs/nfs.h b/usr/src/uts/common/nfs/nfs.h
index 681057e708..109c4fe23c 100644
--- a/usr/src/uts/common/nfs/nfs.h
+++ b/usr/src/uts/common/nfs/nfs.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
@@ -960,6 +960,7 @@ extern kstat_named_t *global_svstat_ptr[];
extern krwlock_t rroklock;
extern vtype_t nf_to_vt[];
extern kstat_named_t *rfsproccnt_v2_ptr;
+extern kstat_t **rfsprocio_v2_ptr;
extern kmutex_t nfs_minor_lock;
extern int nfs_major;
extern int nfs_minor;
@@ -971,13 +972,17 @@ extern void (*nfs_srv_quiesce_func)(void);
extern int (*nfs_srv_dss_func)(char *, size_t);
/*
- * Per-zone stats as consumed by nfsstat(1m)
+ * Per-zone stats
*/
struct nfs_version_stats {
kstat_named_t *aclreqcnt_ptr; /* nfs_acl:0:aclreqcnt_v? */
kstat_named_t *aclproccnt_ptr; /* nfs_acl:0:aclproccnt_v? */
+ kstat_t **aclprocio_ptr; /* nfs_acl:0:aclprocio_v?_* */
+ kmutex_t aclprocio_lock; /* protects aclprocio */
kstat_named_t *rfsreqcnt_ptr; /* nfs:0:rfsreqcnt_v? */
kstat_named_t *rfsproccnt_ptr; /* nfs:0:rfsproccnt_v? */
+ kstat_t **rfsprocio_ptr; /* nfs:0:rfsprocio_v?_* */
+ kmutex_t rfsprocio_lock; /* protects rfsprocio */
};
/*
@@ -1001,6 +1006,30 @@ extern zone_key_t nfsstat_zone_key;
extern void *nfsstat_zone_init(zoneid_t);
extern void nfsstat_zone_fini(zoneid_t, void *);
+/*
+ * Per-exportinfo stats
+ */
+struct exp_kstats {
+ kstat_t *share_kstat; /* Generic share kstat */
+ struct {
+ kstat_named_t path; /* Shared path */
+ kstat_named_t filesystem; /* pseudo|real */
+ } share_kstat_data; /* Generic share kstat data */
+ char *share_path; /* Shared path string */
+ kstat_t **aclprocio_v2_ptr; /* NFS_ACL version 2 */
+ kstat_t **aclprocio_v3_ptr; /* NFS_ACL version 3 */
+ kstat_t **rfsprocio_v2_ptr; /* NFS version 2 */
+ kstat_t **rfsprocio_v3_ptr; /* NFS version 3 */
+ kstat_t **rfsprocio_v4_ptr; /* NFS version 4 */
+ kmutex_t procio_lock; /* protects all exp_kstats */
+};
+
+extern struct exp_kstats *exp_kstats_init(zoneid_t, int, const char *, size_t,
+ bool_t);
+extern void exp_kstats_delete(struct exp_kstats *);
+extern void exp_kstats_fini(struct exp_kstats *);
+extern void exp_kstats_reset(struct exp_kstats *, const char *, size_t, bool_t);
+
#endif /* _KERNEL */
/*
@@ -2281,6 +2310,7 @@ extern int rfs_pathname(char *, vnode_t **, vnode_t **, vnode_t *,
extern vtype_t nf3_to_vt[];
extern kstat_named_t *rfsproccnt_v3_ptr;
+extern kstat_t **rfsprocio_v3_ptr;
extern vfsops_t *nfs3_vfsops;
extern struct vnodeops *nfs3_vnodeops;
extern const struct fs_operation_def nfs3_vnodeops_template[];
diff --git a/usr/src/uts/common/nfs/nfs4.h b/usr/src/uts/common/nfs/nfs4.h
index e27bd42ae4..362a6c9d9a 100644
--- a/usr/src/uts/common/nfs/nfs4.h
+++ b/usr/src/uts/common/nfs/nfs4.h
@@ -1348,6 +1348,7 @@ extern struct nfs4_ntov_map nfs4_ntov_map[];
extern uint_t nfs4_ntov_map_size;
extern kstat_named_t *rfsproccnt_v4_ptr;
+extern kstat_t **rfsprocio_v4_ptr;
extern struct vfsops *nfs4_vfsops;
extern struct vnodeops *nfs4_vnodeops;
extern const struct fs_operation_def nfs4_vnodeops_template[];
@@ -1380,6 +1381,8 @@ extern void rfs4_compound(COMPOUND4args *, COMPOUND4res *,
struct exportinfo *, struct svc_req *, cred_t *, int *);
extern void rfs4_compound_free(COMPOUND4res *);
extern void rfs4_compound_flagproc(COMPOUND4args *, int *);
+extern void rfs4_compound_kstat_args(COMPOUND4args *);
+extern void rfs4_compound_kstat_res(COMPOUND4res *);
extern int rfs4_srvrinit(void);
extern void rfs4_srvrfini(void);
diff --git a/usr/src/uts/common/nfs/nfs4_kprot.h b/usr/src/uts/common/nfs/nfs4_kprot.h
index 30d1e438cd..7f50c1df11 100644
--- a/usr/src/uts/common/nfs/nfs4_kprot.h
+++ b/usr/src/uts/common/nfs/nfs4_kprot.h
@@ -1462,6 +1462,9 @@ struct nfs_argop4 {
WRITE4args opwrite;
RELEASE_LOCKOWNER4args oprelease_lockowner;
} nfs_argop4_u;
+ size_t opsize; /* the number of bytes occupied by the */
+ /* particular operation in the XDR stream */
+ /* (set during the decode only) */
};
typedef struct nfs_argop4 nfs_argop4;
@@ -1508,6 +1511,12 @@ struct nfs_resop4 {
RELEASE_LOCKOWNER4res oprelease_lockowner;
ILLEGAL4res opillegal;
} nfs_resop4_u;
+ size_t opsize; /* the number of bytes occupied by the */
+ /* particular operation in the XDR stream */
+ /* (set during the encode only) */
+ struct exportinfo *exi; /* the exportinfo where the operation should */
+ /* be counted in (support for per-exportinfo */
+ /* kstats) */
};
typedef struct nfs_resop4 nfs_resop4;
diff --git a/usr/src/uts/common/nfs/nfs_acl.h b/usr/src/uts/common/nfs/nfs_acl.h
index a9dd2e3635..7ee7127884 100644
--- a/usr/src/uts/common/nfs/nfs_acl.h
+++ b/usr/src/uts/common/nfs/nfs_acl.h
@@ -18,14 +18,16 @@
*
* CDDL HEADER END
*/
+
+/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
/*
* Copyright 2006 Sun Microsystems, Inc.
* All rights reserved.
* Use is subject to license terms.
*/
-/*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
- */
#ifndef _NFS_NFS_ACL_H
#define _NFS_NFS_ACL_H
@@ -380,7 +382,9 @@ extern void nfs_acl_free(vsecattr_t *);
#ifdef _KERNEL
/* server and client data structures */
extern kstat_named_t *aclproccnt_v2_ptr;
+extern kstat_t **aclprocio_v2_ptr;
extern kstat_named_t *aclproccnt_v3_ptr;
+extern kstat_t **aclprocio_v3_ptr;
extern char *aclnames_v2[];
extern uchar_t acl_call_type_v2[];
diff --git a/usr/src/uts/common/nfs/nfs_dispatch.h b/usr/src/uts/common/nfs/nfs_dispatch.h
index 16475fea47..2a5d56479b 100644
--- a/usr/src/uts/common/nfs/nfs_dispatch.h
+++ b/usr/src/uts/common/nfs/nfs_dispatch.h
@@ -20,11 +20,12 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
+
/*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
/*
@@ -74,6 +75,7 @@ typedef struct rpc_disptable {
int dis_nprocs;
char **dis_procnames;
kstat_named_t **dis_proccntp;
+ kstat_t ***dis_prociop;
struct rpcdisp *dis_table;
} rpc_disptable_t;