diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_dispatch.c | 21 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_srv.c | 244 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_srv_ns.c | 14 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_xdr.c | 41 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_export.c | 120 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_server.c | 95 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_stats.c | 378 | ||||
-rw-r--r-- | usr/src/uts/common/nfs/export.h | 9 | ||||
-rw-r--r-- | usr/src/uts/common/nfs/nfs.h | 34 | ||||
-rw-r--r-- | usr/src/uts/common/nfs/nfs4.h | 3 | ||||
-rw-r--r-- | usr/src/uts/common/nfs/nfs4_kprot.h | 9 | ||||
-rw-r--r-- | usr/src/uts/common/nfs/nfs_acl.h | 10 | ||||
-rw-r--r-- | usr/src/uts/common/nfs/nfs_dispatch.h | 8 |
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; |