Print this page
NEX-16731 Panic in exp_kstats_reset() if exp_kstats is NULL
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
NEX-16917 Need to reduce the impact of NFS per-share kstats on failover
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
NEX-6778 NFS kstats leak and cause system to hang
Revert "NEX-4261 Per-client NFS server IOPS, bandwidth, and latency kstats"
This reverts commit 586c3ab1927647487f01c337ddc011c642575a52.
Revert "NEX-5354 Aggregated IOPS, bandwidth, and latency kstats for NFS server"
This reverts commit c91d7614da8618ef48018102b077f60ecbbac8c2.
Revert "NEX-5667 nfssrv_stats_flags does not work for aggregated kstats"
This reverts commit 3dcf42618be7dd5f408c327f429c81e07ca08e74.
Revert "NEX-5750 Time values for aggregated NFS server kstats should be normalized"
This reverts commit 1f4d4f901153b0191027969fa4a8064f9d3b9ee1.
Revert "NEX-5942 Panic in rfs4_minorvers_mismatch() with NFSv4.1 client"
This reverts commit 40766417094a162f5e4cc8786c0fa0a7e5871cd9.
Revert "NEX-5752 NFS server: namespace collision in kstats"
This reverts commit ae81e668db86050da8e483264acb0cce0444a132.
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-4261 Per-client NFS server IOPS, bandwidth, and latency kstats
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-3097 IOPS, bandwidth, and latency kstats for NFS server
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
@@ -16,11 +16,16 @@
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
+
/*
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/types.h>
@@ -43,12 +48,11 @@
* module:0:name:"misc", using the provided template to initialize the names
* and values of the stats.
*/
static kstat_named_t *
nfsstat_zone_init_common(zoneid_t zoneid, const char *module, int vers,
- const char *name, const kstat_named_t *template,
- size_t template_size)
+ const char *name, const kstat_named_t *template, size_t template_size)
{
kstat_t *ksp;
kstat_named_t *ks_data;
ks_data = kmem_alloc(template_size, KM_SLEEP);
@@ -125,10 +129,57 @@
kmem_free(svstatp[vers], sizeof (svstat_tmpl));
}
}
/*
+ * 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[] = {
{ "null", KSTAT_DATA_UINT64 },
{ "getattr", KSTAT_DATA_UINT64 },
@@ -186,31 +237,48 @@
{ "rmdir", KSTAT_DATA_UINT64 },
{ "readdir", KSTAT_DATA_UINT64 },
{ "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);
}
/*
* NFSv2 client ACL stats
*/
@@ -247,32 +315,48 @@
{ "getattr", KSTAT_DATA_UINT64 },
{ "access", KSTAT_DATA_UINT64 },
{ "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);
}
/*
* NFSv3 client stats
*/
@@ -341,31 +425,48 @@
{ "fsinfo", KSTAT_DATA_UINT64 },
{ "pathconf", KSTAT_DATA_UINT64 },
{ "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);
}
/*
* NFSv3 client ACL stats
*/
@@ -398,32 +499,48 @@
{ "getacl", KSTAT_DATA_UINT64 },
{ "setacl", KSTAT_DATA_UINT64 },
{ "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);
}
/*
* NFSv4 client stats
*/
@@ -528,31 +645,48 @@
{ "write", KSTAT_DATA_UINT64 },
{ "release_lockowner", KSTAT_DATA_UINT64 },
{ "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);
}
/*
* NFSv4 client ACL stats
*/
@@ -607,11 +741,26 @@
aclproccnt_v4_ptr = NULL;
nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclproccnt_v4");
kmem_free(statsp->aclproccnt_ptr, sizeof (aclproccnt_v4_tmpl));
}
+
/*
+ * NFS server per share kstats (exp_kstats)
+ * kstats are collected per share for NFSv3 & NFSv4 read and write operations.
+ */
+#define NFSSRV_SHR_READ 0
+#define NFSSRV_SHR_WRITE 1
+
+static const kstat_named_t rfsshr_tmpl[] = {
+ { "read", KSTAT_DATA_UINT64 }, /* NFSSRV_SHR_READ */
+ { "write", KSTAT_DATA_UINT64 } /* NFSSRV_SHR_WRITE */
+};
+#define RFSSHARE_COUNT \
+ (sizeof (rfsshr_tmpl) / sizeof (rfsshr_tmpl[0]))
+
+/*
* Zone initializer callback to setup the kstats.
*/
void *
nfsstat_zone_init(zoneid_t zoneid)
{
@@ -683,6 +832,169 @@
nfsstat_zone_fini_rfsproc_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
nfsstat_zone_fini_aclreq_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
nfsstat_zone_fini_aclproc_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
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 version 3 */
+ exp_kstats->rfsshr_v3_ptr = rfs_kstat_io_init(zoneid, "nfs",
+ instance, "share_v3", "rfsprocio_v3", rfsshr_tmpl,
+ RFSSHARE_COUNT, &exp_kstats->procio_lock);
+
+ /* NFS version 4 */
+ exp_kstats->rfsshr_v4_ptr = rfs_kstat_io_init(zoneid, "nfs",
+ instance, "share_v4", "rfsprocio_v4", rfsshr_tmpl,
+ RFSSHARE_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);
+ }
+
+ rfs_kstat_io_delete(exp_kstats->rfsshr_v3_ptr, RFSSHARE_COUNT);
+ rfs_kstat_io_delete(exp_kstats->rfsshr_v4_ptr, RFSSHARE_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);
+ }
+
+ rfs_kstat_io_free(exp_kstats->rfsshr_v3_ptr, RFSSHARE_COUNT);
+ rfs_kstat_io_free(exp_kstats->rfsshr_v4_ptr, RFSSHARE_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 == NULL) || (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);
+}
+
+kstat_t *
+/* LINTED E_FUNC_ARG_UNUSED */
+exp_kstats_v2(struct exp_kstats *exp_kstats, uint_t op)
+{
+ /* No NFS v2 per-share kstats */
+ return (NULL);
+}
+
+kstat_t *
+exp_kstats_v3(struct exp_kstats *exp_kstats, uint_t op)
+{
+ if (exp_kstats == NULL)
+ return (NULL);
+
+ /* per share kstats for selected operations (read, write) only */
+ switch (op) {
+ case NFSPROC3_READ:
+ return (exp_kstats->rfsshr_v3_ptr[NFSSRV_SHR_READ]);
+ case NFSPROC3_WRITE:
+ return (exp_kstats->rfsshr_v3_ptr[NFSSRV_SHR_WRITE]);
+ default:
+ return (NULL);
+ }
+}
+
+kstat_t *
+exp_kstats_v4(struct exp_kstats *exp_kstats, uint_t op)
+{
+ if (exp_kstats == NULL)
+ return (NULL);
+
+ /* per share kstats for selected operations (read, write) only */
+ switch (op) {
+ case OP_READ:
+ return (exp_kstats->rfsshr_v4_ptr[NFSSRV_SHR_READ]);
+ case OP_WRITE:
+ return (exp_kstats->rfsshr_v4_ptr[NFSSRV_SHR_WRITE]);
+ default:
+ return (NULL);
+ }
}