xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs_stats.c (revision 45818ee124adeaaf947698996b4f4c722afc6d1f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/kstat.h>
33 #include <sys/zone.h>
34 #include <sys/kmem.h>
35 #include <sys/systm.h>
36 
37 #include <nfs/nfs.h>
38 #include <nfs/nfs4_kprot.h>
39 
40 /*
41  * Key to retrieve per-zone data corresponding to NFS kstats consumed by
42  * nfsstat(1m).
43  */
44 zone_key_t nfsstat_zone_key;
45 
46 /*
47  * Convenience routine to create a named kstat associated with zoneid, named
48  * module:0:name:"misc", using the provided template to initialize the names
49  * and values of the stats.
50  */
51 static kstat_named_t *
52 nfsstat_zone_init_common(zoneid_t zoneid, const char *module, int vers,
53 			    const char *name, const kstat_named_t *template,
54 			    size_t template_size)
55 {
56 	kstat_t *ksp;
57 	kstat_named_t *ks_data;
58 
59 	ks_data = kmem_alloc(template_size, KM_SLEEP);
60 	bcopy(template, ks_data, template_size);
61 	if ((ksp = kstat_create_zone(module, vers, name, "misc",
62 	    KSTAT_TYPE_NAMED, template_size / sizeof (kstat_named_t),
63 	    KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE, zoneid)) != NULL) {
64 		ksp->ks_data = ks_data;
65 		kstat_install(ksp);
66 	}
67 	return (ks_data);
68 }
69 
70 /*
71  * Convenience routine to remove a kstat in specified zone with name
72  * module:0:name.
73  */
74 static void
75 nfsstat_zone_fini_common(zoneid_t zoneid, const char *module, int vers,
76 			    const char *name)
77 {
78 	kstat_delete_byname_zone(module, vers, name, zoneid);
79 }
80 
81 /*
82  * Server statistics.  These are defined here, rather than in the server
83  * code, so that they can be referenced before the nfssrv kmod is loaded.
84  *
85  * The "calls" counter is a Contract Private interface covered by
86  * PSARC/2001/357.  Please contact contract-2001-357-01@eng.sun.com before
87  * making any changes.
88  */
89 
90 static const kstat_named_t svstat_tmpl[] = {
91 	{ "calls",	KSTAT_DATA_UINT64 },
92 	{ "badcalls",	KSTAT_DATA_UINT64 },
93 	{ "referrals",	KSTAT_DATA_UINT64 },
94 	{ "referlinks",	KSTAT_DATA_UINT64 },
95 };
96 
97 /* Points to the global zone server kstat data for all nfs versions */
98 kstat_named_t *global_svstat_ptr[NFS_VERSMAX + 1];
99 
100 static void
101 nfsstat_zone_init_server(zoneid_t zoneid, kstat_named_t *svstatp[])
102 {
103 	int vers;
104 
105 	/*
106 	 * first two indexes of these arrays are not used, so initialize
107 	 * to NULL
108 	 */
109 	svstatp[0] = NULL;
110 	svstatp[1] = NULL;
111 	global_svstat_ptr[0] = NULL;
112 	global_svstat_ptr[0] = NULL;
113 
114 	for (vers = NFS_VERSION; vers <= NFS_V4; vers++) {
115 		svstatp[vers] = nfsstat_zone_init_common(zoneid, "nfs", vers,
116 		    "nfs_server", svstat_tmpl, sizeof (svstat_tmpl));
117 		if (zoneid == GLOBAL_ZONEID)
118 			global_svstat_ptr[vers] = svstatp[vers];
119 	}
120 }
121 
122 static void
123 nfsstat_zone_fini_server(zoneid_t zoneid, kstat_named_t **svstatp)
124 {
125 	int vers;
126 	for (vers = NFS_VERSION; vers <= NFS_V4; vers++) {
127 		if (zoneid == GLOBAL_ZONEID)
128 			global_svstat_ptr[vers] = NULL;
129 		nfsstat_zone_fini_common(zoneid, "nfs", vers, "nfs_server");
130 		kmem_free(svstatp[vers], sizeof (svstat_tmpl));
131 	}
132 }
133 
134 /*
135  * Support functions for the kstat_io alloc/free
136  */
137 static kstat_t **
138 rfs_kstat_io_init(zoneid_t zoneid, const char *module, int instance,
139     const char *name, const char *class, const kstat_named_t *tmpl, int count,
140     kmutex_t *lock)
141 {
142 	int i;
143 	kstat_t **ret = kmem_alloc(count * sizeof (*ret), KM_SLEEP);
144 
145 	for (i = 0; i < count; i++) {
146 		char namebuf[KSTAT_STRLEN];
147 
148 		(void) snprintf(namebuf, sizeof (namebuf), "%s_%s", name,
149 		    tmpl[i].name);
150 		ret[i] = kstat_create_zone(module, instance, namebuf, class,
151 		    KSTAT_TYPE_IO, 1, 0, zoneid);
152 		if (ret[i] != NULL) {
153 			ret[i]->ks_lock = lock;
154 			kstat_install(ret[i]);
155 		}
156 	}
157 
158 	return (ret);
159 }
160 
161 static void
162 rfs_kstat_io_delete(kstat_t **ks, int count)
163 {
164 	int i;
165 
166 	for (i = 0; i < count; i++) {
167 		if (ks[i] != NULL) {
168 			kstat_delete(ks[i]);
169 			ks[i] = NULL;
170 		}
171 	}
172 }
173 
174 static void
175 rfs_kstat_io_free(kstat_t **ks, int count)
176 {
177 	rfs_kstat_io_delete(ks, count);
178 	kmem_free(ks, count * sizeof (*ks));
179 }
180 
181 /*
182  * NFSv2 client stats
183  */
184 static const kstat_named_t rfsreqcnt_v2_tmpl[] = {
185 	{ "null",	KSTAT_DATA_UINT64 },
186 	{ "getattr",	KSTAT_DATA_UINT64 },
187 	{ "setattr",	KSTAT_DATA_UINT64 },
188 	{ "root",	KSTAT_DATA_UINT64 },
189 	{ "lookup",	KSTAT_DATA_UINT64 },
190 	{ "readlink",	KSTAT_DATA_UINT64 },
191 	{ "read",	KSTAT_DATA_UINT64 },
192 	{ "wrcache",	KSTAT_DATA_UINT64 },
193 	{ "write",	KSTAT_DATA_UINT64 },
194 	{ "create",	KSTAT_DATA_UINT64 },
195 	{ "remove",	KSTAT_DATA_UINT64 },
196 	{ "rename",	KSTAT_DATA_UINT64 },
197 	{ "link",	KSTAT_DATA_UINT64 },
198 	{ "symlink",	KSTAT_DATA_UINT64 },
199 	{ "mkdir",	KSTAT_DATA_UINT64 },
200 	{ "rmdir",	KSTAT_DATA_UINT64 },
201 	{ "readdir",	KSTAT_DATA_UINT64 },
202 	{ "statfs",	KSTAT_DATA_UINT64 }
203 };
204 
205 static void
206 nfsstat_zone_init_rfsreq_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
207 {
208 	statsp->rfsreqcnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
209 	    "rfsreqcnt_v2", rfsreqcnt_v2_tmpl, sizeof (rfsreqcnt_v2_tmpl));
210 }
211 
212 static void
213 nfsstat_zone_fini_rfsreq_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
214 {
215 	nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsreqcnt_v2");
216 	kmem_free(statsp->rfsreqcnt_ptr, sizeof (rfsreqcnt_v2_tmpl));
217 }
218 
219 /*
220  * NFSv2 server stats
221  */
222 static const kstat_named_t rfsproccnt_v2_tmpl[] = {
223 	{ "null",	KSTAT_DATA_UINT64 },
224 	{ "getattr",	KSTAT_DATA_UINT64 },
225 	{ "setattr",	KSTAT_DATA_UINT64 },
226 	{ "root",	KSTAT_DATA_UINT64 },
227 	{ "lookup",	KSTAT_DATA_UINT64 },
228 	{ "readlink",	KSTAT_DATA_UINT64 },
229 	{ "read",	KSTAT_DATA_UINT64 },
230 	{ "wrcache",	KSTAT_DATA_UINT64 },
231 	{ "write",	KSTAT_DATA_UINT64 },
232 	{ "create",	KSTAT_DATA_UINT64 },
233 	{ "remove",	KSTAT_DATA_UINT64 },
234 	{ "rename",	KSTAT_DATA_UINT64 },
235 	{ "link",	KSTAT_DATA_UINT64 },
236 	{ "symlink",	KSTAT_DATA_UINT64 },
237 	{ "mkdir",	KSTAT_DATA_UINT64 },
238 	{ "rmdir",	KSTAT_DATA_UINT64 },
239 	{ "readdir",	KSTAT_DATA_UINT64 },
240 	{ "statfs",	KSTAT_DATA_UINT64 }
241 };
242 
243 #define	RFSPROCCNT_V2_COUNT	\
244 	(sizeof (rfsproccnt_v2_tmpl) / sizeof (rfsproccnt_v2_tmpl[0]))
245 
246 kstat_named_t *rfsproccnt_v2_ptr;
247 kstat_t **rfsprocio_v2_ptr;
248 
249 static void
250 nfsstat_zone_init_rfsproc_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
251 {
252 	statsp->rfsproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
253 	    "rfsproccnt_v2", rfsproccnt_v2_tmpl, sizeof (rfsproccnt_v2_tmpl));
254 
255 	mutex_init(&statsp->rfsprocio_lock, NULL, MUTEX_DEFAULT, NULL);
256 
257 	statsp->rfsprocio_ptr = rfs_kstat_io_init(zoneid, "nfs", 0,
258 	    "rfsprocio_v2", "rfsprocio_v2", rfsproccnt_v2_tmpl,
259 	    RFSPROCCNT_V2_COUNT, &statsp->rfsprocio_lock);
260 
261 	if (zoneid == GLOBAL_ZONEID) {
262 		rfsproccnt_v2_ptr = statsp->rfsproccnt_ptr;
263 		rfsprocio_v2_ptr = statsp->rfsprocio_ptr;
264 	}
265 }
266 
267 static void
268 nfsstat_zone_fini_rfsproc_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
269 {
270 	if (zoneid == GLOBAL_ZONEID) {
271 		rfsproccnt_v2_ptr = NULL;
272 		rfsprocio_v2_ptr = NULL;
273 	}
274 
275 	nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsproccnt_v2");
276 	kmem_free(statsp->rfsproccnt_ptr, sizeof (rfsproccnt_v2_tmpl));
277 
278 	rfs_kstat_io_free(statsp->rfsprocio_ptr, RFSPROCCNT_V2_COUNT);
279 
280 	mutex_destroy(&statsp->rfsprocio_lock);
281 }
282 
283 /*
284  * NFSv2 client ACL stats
285  */
286 static const kstat_named_t aclreqcnt_v2_tmpl[] = {
287 	{ "null",	KSTAT_DATA_UINT64 },
288 	{ "getacl",	KSTAT_DATA_UINT64 },
289 	{ "setacl",	KSTAT_DATA_UINT64 },
290 	{ "getattr",	KSTAT_DATA_UINT64 },
291 	{ "access",	KSTAT_DATA_UINT64 },
292 	{ "getxattrdir",	KSTAT_DATA_UINT64 }
293 };
294 
295 static void
296 nfsstat_zone_init_aclreq_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
297 {
298 	statsp->aclreqcnt_ptr = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
299 	    "aclreqcnt_v2", aclreqcnt_v2_tmpl, sizeof (aclreqcnt_v2_tmpl));
300 }
301 
302 static void
303 nfsstat_zone_fini_aclreq_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
304 {
305 	nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclreqcnt_v2");
306 	kmem_free(statsp->aclreqcnt_ptr, sizeof (aclreqcnt_v2_tmpl));
307 }
308 
309 /*
310  * NFSv2 server ACL stats
311  */
312 static const kstat_named_t aclproccnt_v2_tmpl[] = {
313 	{ "null",	KSTAT_DATA_UINT64 },
314 	{ "getacl",	KSTAT_DATA_UINT64 },
315 	{ "setacl",	KSTAT_DATA_UINT64 },
316 	{ "getattr",	KSTAT_DATA_UINT64 },
317 	{ "access",	KSTAT_DATA_UINT64 },
318 	{ "getxattrdir",	KSTAT_DATA_UINT64 }
319 };
320 
321 #define	ACLPROCCNT_V2_COUNT	\
322 	(sizeof (aclproccnt_v2_tmpl) / sizeof (aclproccnt_v2_tmpl[0]))
323 
324 kstat_named_t *aclproccnt_v2_ptr;
325 kstat_t **aclprocio_v2_ptr;
326 
327 static void
328 nfsstat_zone_init_aclproc_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
329 {
330 	statsp->aclproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
331 	    "aclproccnt_v2", aclproccnt_v2_tmpl, sizeof (aclproccnt_v2_tmpl));
332 
333 	mutex_init(&statsp->aclprocio_lock, NULL, MUTEX_DEFAULT, NULL);
334 
335 	statsp->aclprocio_ptr = rfs_kstat_io_init(zoneid, "nfs_acl", 0,
336 	    "aclprocio_v2", "aclprocio_v2", aclproccnt_v2_tmpl,
337 	    ACLPROCCNT_V2_COUNT, &statsp->aclprocio_lock);
338 
339 	if (zoneid == GLOBAL_ZONEID) {
340 		aclproccnt_v2_ptr = statsp->aclproccnt_ptr;
341 		aclprocio_v2_ptr = statsp->aclprocio_ptr;
342 	}
343 }
344 
345 static void
346 nfsstat_zone_fini_aclproc_v2(zoneid_t zoneid, struct nfs_version_stats *statsp)
347 {
348 	if (zoneid == GLOBAL_ZONEID) {
349 		aclproccnt_v2_ptr = NULL;
350 		aclprocio_v2_ptr = NULL;
351 	}
352 
353 	nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclproccnt_v2");
354 	kmem_free(statsp->aclproccnt_ptr, sizeof (aclproccnt_v2_tmpl));
355 
356 	rfs_kstat_io_free(statsp->aclprocio_ptr, ACLPROCCNT_V2_COUNT);
357 
358 	mutex_destroy(&statsp->aclprocio_lock);
359 }
360 
361 /*
362  * NFSv3 client stats
363  */
364 static const kstat_named_t rfsreqcnt_v3_tmpl[] = {
365 	{ "null",	KSTAT_DATA_UINT64 },
366 	{ "getattr",	KSTAT_DATA_UINT64 },
367 	{ "setattr",	KSTAT_DATA_UINT64 },
368 	{ "lookup",	KSTAT_DATA_UINT64 },
369 	{ "access",	KSTAT_DATA_UINT64 },
370 	{ "readlink",	KSTAT_DATA_UINT64 },
371 	{ "read",	KSTAT_DATA_UINT64 },
372 	{ "write",	KSTAT_DATA_UINT64 },
373 	{ "create",	KSTAT_DATA_UINT64 },
374 	{ "mkdir",	KSTAT_DATA_UINT64 },
375 	{ "symlink",	KSTAT_DATA_UINT64 },
376 	{ "mknod",	KSTAT_DATA_UINT64 },
377 	{ "remove",	KSTAT_DATA_UINT64 },
378 	{ "rmdir",	KSTAT_DATA_UINT64 },
379 	{ "rename",	KSTAT_DATA_UINT64 },
380 	{ "link",	KSTAT_DATA_UINT64 },
381 	{ "readdir",	KSTAT_DATA_UINT64 },
382 	{ "readdirplus", KSTAT_DATA_UINT64 },
383 	{ "fsstat",	KSTAT_DATA_UINT64 },
384 	{ "fsinfo",	KSTAT_DATA_UINT64 },
385 	{ "pathconf",	KSTAT_DATA_UINT64 },
386 	{ "commit",	KSTAT_DATA_UINT64 }
387 };
388 
389 static void
390 nfsstat_zone_init_rfsreq_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
391 {
392 	statsp->rfsreqcnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
393 	    "rfsreqcnt_v3", rfsreqcnt_v3_tmpl, sizeof (rfsreqcnt_v3_tmpl));
394 }
395 
396 static void
397 nfsstat_zone_fini_rfsreq_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
398 {
399 	nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsreqcnt_v3");
400 	kmem_free(statsp->rfsreqcnt_ptr, sizeof (rfsreqcnt_v3_tmpl));
401 }
402 
403 /*
404  * NFSv3 server stats
405  */
406 static const kstat_named_t rfsproccnt_v3_tmpl[] = {
407 	{ "null",	KSTAT_DATA_UINT64 },
408 	{ "getattr",	KSTAT_DATA_UINT64 },
409 	{ "setattr",	KSTAT_DATA_UINT64 },
410 	{ "lookup",	KSTAT_DATA_UINT64 },
411 	{ "access",	KSTAT_DATA_UINT64 },
412 	{ "readlink",	KSTAT_DATA_UINT64 },
413 	{ "read",	KSTAT_DATA_UINT64 },
414 	{ "write",	KSTAT_DATA_UINT64 },
415 	{ "create",	KSTAT_DATA_UINT64 },
416 	{ "mkdir",	KSTAT_DATA_UINT64 },
417 	{ "symlink",	KSTAT_DATA_UINT64 },
418 	{ "mknod",	KSTAT_DATA_UINT64 },
419 	{ "remove",	KSTAT_DATA_UINT64 },
420 	{ "rmdir",	KSTAT_DATA_UINT64 },
421 	{ "rename",	KSTAT_DATA_UINT64 },
422 	{ "link",	KSTAT_DATA_UINT64 },
423 	{ "readdir",	KSTAT_DATA_UINT64 },
424 	{ "readdirplus", KSTAT_DATA_UINT64 },
425 	{ "fsstat",	KSTAT_DATA_UINT64 },
426 	{ "fsinfo",	KSTAT_DATA_UINT64 },
427 	{ "pathconf",	KSTAT_DATA_UINT64 },
428 	{ "commit",	KSTAT_DATA_UINT64 }
429 };
430 
431 #define	RFSPROCCNT_V3_COUNT	\
432 	(sizeof (rfsproccnt_v3_tmpl) / sizeof (rfsproccnt_v3_tmpl[0]))
433 
434 kstat_named_t *rfsproccnt_v3_ptr;
435 kstat_t **rfsprocio_v3_ptr;
436 
437 static void
438 nfsstat_zone_init_rfsproc_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
439 {
440 	statsp->rfsproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
441 	    "rfsproccnt_v3", rfsproccnt_v3_tmpl, sizeof (rfsproccnt_v3_tmpl));
442 
443 	mutex_init(&statsp->rfsprocio_lock, NULL, MUTEX_DEFAULT, NULL);
444 
445 	statsp->rfsprocio_ptr = rfs_kstat_io_init(zoneid, "nfs", 0,
446 	    "rfsprocio_v3", "rfsprocio_v3", rfsproccnt_v3_tmpl,
447 	    RFSPROCCNT_V3_COUNT, &statsp->rfsprocio_lock);
448 
449 	if (zoneid == GLOBAL_ZONEID) {
450 		rfsproccnt_v3_ptr = statsp->rfsproccnt_ptr;
451 		rfsprocio_v3_ptr = statsp->rfsprocio_ptr;
452 	}
453 }
454 
455 static void
456 nfsstat_zone_fini_rfsproc_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
457 {
458 	if (zoneid == GLOBAL_ZONEID) {
459 		rfsproccnt_v3_ptr = NULL;
460 		rfsprocio_v3_ptr = NULL;
461 	}
462 
463 	nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsproccnt_v3");
464 	kmem_free(statsp->rfsproccnt_ptr, sizeof (rfsproccnt_v3_tmpl));
465 
466 	rfs_kstat_io_free(statsp->rfsprocio_ptr, RFSPROCCNT_V3_COUNT);
467 
468 	mutex_destroy(&statsp->rfsprocio_lock);
469 }
470 
471 /*
472  * NFSv3 client ACL stats
473  */
474 static const kstat_named_t aclreqcnt_v3_tmpl[] = {
475 	{ "null",	KSTAT_DATA_UINT64 },
476 	{ "getacl",	KSTAT_DATA_UINT64 },
477 	{ "setacl",	KSTAT_DATA_UINT64 },
478 	{ "getxattrdir",	KSTAT_DATA_UINT64 }
479 };
480 
481 static void
482 nfsstat_zone_init_aclreq_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
483 {
484 	statsp->aclreqcnt_ptr = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
485 	    "aclreqcnt_v3", aclreqcnt_v3_tmpl, sizeof (aclreqcnt_v3_tmpl));
486 }
487 
488 static void
489 nfsstat_zone_fini_aclreq_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
490 {
491 	nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclreqcnt_v3");
492 	kmem_free(statsp->aclreqcnt_ptr, sizeof (aclreqcnt_v3_tmpl));
493 }
494 
495 /*
496  * NFSv3 server ACL stats
497  */
498 static const kstat_named_t aclproccnt_v3_tmpl[] = {
499 	{ "null",	KSTAT_DATA_UINT64 },
500 	{ "getacl",	KSTAT_DATA_UINT64 },
501 	{ "setacl",	KSTAT_DATA_UINT64 },
502 	{ "getxattrdir",	KSTAT_DATA_UINT64 }
503 };
504 
505 #define	ACLPROCCNT_V3_COUNT	\
506 	(sizeof (aclproccnt_v3_tmpl) / sizeof (aclproccnt_v3_tmpl[0]))
507 
508 kstat_named_t *aclproccnt_v3_ptr;
509 kstat_t **aclprocio_v3_ptr;
510 
511 static void
512 nfsstat_zone_init_aclproc_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
513 {
514 	statsp->aclproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
515 	    "aclproccnt_v3", aclproccnt_v3_tmpl, sizeof (aclproccnt_v3_tmpl));
516 
517 	mutex_init(&statsp->aclprocio_lock, NULL, MUTEX_DEFAULT, NULL);
518 
519 	statsp->aclprocio_ptr = rfs_kstat_io_init(zoneid, "nfs_acl", 0,
520 	    "aclprocio_v3", "aclprocio_v3", aclproccnt_v3_tmpl,
521 	    ACLPROCCNT_V3_COUNT, &statsp->aclprocio_lock);
522 
523 	if (zoneid == GLOBAL_ZONEID) {
524 		aclproccnt_v3_ptr = statsp->aclproccnt_ptr;
525 		aclprocio_v3_ptr = statsp->aclprocio_ptr;
526 	}
527 }
528 
529 static void
530 nfsstat_zone_fini_aclproc_v3(zoneid_t zoneid, struct nfs_version_stats *statsp)
531 {
532 	if (zoneid == GLOBAL_ZONEID) {
533 		aclproccnt_v3_ptr = NULL;
534 		aclprocio_v3_ptr = NULL;
535 	}
536 
537 	nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclproccnt_v3");
538 	kmem_free(statsp->aclproccnt_ptr, sizeof (aclproccnt_v3_tmpl));
539 
540 	rfs_kstat_io_free(statsp->aclprocio_ptr, ACLPROCCNT_V3_COUNT);
541 
542 	mutex_destroy(&statsp->aclprocio_lock);
543 }
544 
545 /*
546  * NFSv4 client stats
547  */
548 static const kstat_named_t rfsreqcnt_v4_tmpl[] = {
549 	{ "null",	KSTAT_DATA_UINT64 },
550 	{ "compound",	KSTAT_DATA_UINT64 },
551 	{ "reserved",	KSTAT_DATA_UINT64 },
552 	{ "access",	KSTAT_DATA_UINT64 },
553 	{ "close",	KSTAT_DATA_UINT64 },
554 	{ "commit",	KSTAT_DATA_UINT64 },
555 	{ "create",	KSTAT_DATA_UINT64 },
556 	{ "delegpurge",	KSTAT_DATA_UINT64 },
557 	{ "delegreturn",	KSTAT_DATA_UINT64 },
558 	{ "getattr",	KSTAT_DATA_UINT64 },
559 	{ "getfh",	KSTAT_DATA_UINT64 },
560 	{ "link",	KSTAT_DATA_UINT64 },
561 	{ "lock",	KSTAT_DATA_UINT64 },
562 	{ "lockt",	KSTAT_DATA_UINT64 },
563 	{ "locku",	KSTAT_DATA_UINT64 },
564 	{ "lookup",	KSTAT_DATA_UINT64 },
565 	{ "lookupp",	KSTAT_DATA_UINT64 },
566 	{ "nverify",	KSTAT_DATA_UINT64 },
567 	{ "open",	KSTAT_DATA_UINT64 },
568 	{ "openattr",	KSTAT_DATA_UINT64 },
569 	{ "open_confirm",	KSTAT_DATA_UINT64 },
570 	{ "open_downgrade",	KSTAT_DATA_UINT64 },
571 	{ "putfh",	KSTAT_DATA_UINT64 },
572 	{ "putpubfh",	KSTAT_DATA_UINT64 },
573 	{ "putrootfh",	KSTAT_DATA_UINT64 },
574 	{ "read",	KSTAT_DATA_UINT64 },
575 	{ "readdir",	KSTAT_DATA_UINT64 },
576 	{ "readlink",	KSTAT_DATA_UINT64 },
577 	{ "remove",	KSTAT_DATA_UINT64 },
578 	{ "rename",	KSTAT_DATA_UINT64 },
579 	{ "renew",	KSTAT_DATA_UINT64 },
580 	{ "restorefh",	KSTAT_DATA_UINT64 },
581 	{ "savefh",	KSTAT_DATA_UINT64 },
582 	{ "secinfo",	KSTAT_DATA_UINT64 },
583 	{ "setattr",	KSTAT_DATA_UINT64 },
584 	{ "setclientid",	KSTAT_DATA_UINT64 },
585 	{ "setclientid_confirm",	KSTAT_DATA_UINT64 },
586 	{ "verify", KSTAT_DATA_UINT64 },
587 	{ "write",	KSTAT_DATA_UINT64 }
588 };
589 
590 static void
591 nfsstat_zone_init_rfsreq_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
592 {
593 	statsp->rfsreqcnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
594 	    "rfsreqcnt_v4", rfsreqcnt_v4_tmpl, sizeof (rfsreqcnt_v4_tmpl));
595 }
596 
597 static void
598 nfsstat_zone_fini_rfsreq_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
599 {
600 	nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsreqcnt_v4");
601 	kmem_free(statsp->rfsreqcnt_ptr, sizeof (rfsreqcnt_v4_tmpl));
602 }
603 
604 /*
605  * NFSv4 server stats
606  */
607 static const kstat_named_t rfsproccnt_v4_tmpl[] = {
608 	{ "null",	KSTAT_DATA_UINT64 },
609 	{ "compound",	KSTAT_DATA_UINT64 },
610 	{ "reserved",	KSTAT_DATA_UINT64 },
611 	{ "access",	KSTAT_DATA_UINT64 },
612 	{ "close",	KSTAT_DATA_UINT64 },
613 	{ "commit",	KSTAT_DATA_UINT64 },
614 	{ "create",	KSTAT_DATA_UINT64 },
615 	{ "delegpurge",	KSTAT_DATA_UINT64 },
616 	{ "delegreturn",	KSTAT_DATA_UINT64 },
617 	{ "getattr",	KSTAT_DATA_UINT64 },
618 	{ "getfh",	KSTAT_DATA_UINT64 },
619 	{ "link",	KSTAT_DATA_UINT64 },
620 	{ "lock",	KSTAT_DATA_UINT64 },
621 	{ "lockt",	KSTAT_DATA_UINT64 },
622 	{ "locku",	KSTAT_DATA_UINT64 },
623 	{ "lookup",	KSTAT_DATA_UINT64 },
624 	{ "lookupp",	KSTAT_DATA_UINT64 },
625 	{ "nverify",	KSTAT_DATA_UINT64 },
626 	{ "open",	KSTAT_DATA_UINT64 },
627 	{ "openattr",	KSTAT_DATA_UINT64 },
628 	{ "open_confirm",	KSTAT_DATA_UINT64 },
629 	{ "open_downgrade",	KSTAT_DATA_UINT64 },
630 	{ "putfh",	KSTAT_DATA_UINT64 },
631 	{ "putpubfh",	KSTAT_DATA_UINT64 },
632 	{ "putrootfh",	KSTAT_DATA_UINT64 },
633 	{ "read",	KSTAT_DATA_UINT64 },
634 	{ "readdir",	KSTAT_DATA_UINT64 },
635 	{ "readlink",	KSTAT_DATA_UINT64 },
636 	{ "remove",	KSTAT_DATA_UINT64 },
637 	{ "rename",	KSTAT_DATA_UINT64 },
638 	{ "renew",	KSTAT_DATA_UINT64 },
639 	{ "restorefh",	KSTAT_DATA_UINT64 },
640 	{ "savefh",	KSTAT_DATA_UINT64 },
641 	{ "secinfo",	KSTAT_DATA_UINT64 },
642 	{ "setattr",	KSTAT_DATA_UINT64 },
643 	{ "setclientid",	KSTAT_DATA_UINT64 },
644 	{ "setclientid_confirm",	KSTAT_DATA_UINT64 },
645 	{ "verify",	KSTAT_DATA_UINT64 },
646 	{ "write",	KSTAT_DATA_UINT64 },
647 	{ "release_lockowner",	KSTAT_DATA_UINT64 },
648 	{ "illegal",	KSTAT_DATA_UINT64 },
649 };
650 
651 #define	RFSPROCCNT_V4_COUNT	\
652 	(sizeof (rfsproccnt_v4_tmpl) / sizeof (rfsproccnt_v4_tmpl[0]))
653 
654 kstat_named_t *rfsproccnt_v4_ptr;
655 kstat_t **rfsprocio_v4_ptr;
656 
657 static void
658 nfsstat_zone_init_rfsproc_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
659 {
660 	statsp->rfsproccnt_ptr = nfsstat_zone_init_common(zoneid, "nfs", 0,
661 	    "rfsproccnt_v4", rfsproccnt_v4_tmpl, sizeof (rfsproccnt_v4_tmpl));
662 
663 	mutex_init(&statsp->rfsprocio_lock, NULL, MUTEX_DEFAULT, NULL);
664 
665 	statsp->rfsprocio_ptr = rfs_kstat_io_init(zoneid, "nfs", 0,
666 	    "rfsprocio_v4", "rfsprocio_v4", rfsproccnt_v4_tmpl,
667 	    RFSPROCCNT_V4_COUNT, &statsp->rfsprocio_lock);
668 
669 	if (zoneid == GLOBAL_ZONEID) {
670 		rfsproccnt_v4_ptr = statsp->rfsproccnt_ptr;
671 		rfsprocio_v4_ptr = statsp->rfsprocio_ptr;
672 	}
673 }
674 
675 static void
676 nfsstat_zone_fini_rfsproc_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
677 {
678 	if (zoneid == GLOBAL_ZONEID) {
679 		rfsproccnt_v4_ptr = NULL;
680 		rfsprocio_v4_ptr = NULL;
681 	}
682 
683 	nfsstat_zone_fini_common(zoneid, "nfs", 0, "rfsproccnt_v4");
684 	kmem_free(statsp->rfsproccnt_ptr, sizeof (rfsproccnt_v4_tmpl));
685 
686 	rfs_kstat_io_free(statsp->rfsprocio_ptr, RFSPROCCNT_V4_COUNT);
687 
688 	mutex_destroy(&statsp->rfsprocio_lock);
689 }
690 
691 /*
692  * NFSv4 client ACL stats
693  */
694 static const kstat_named_t aclreqcnt_v4_tmpl[] = {
695 	{ "null",	KSTAT_DATA_UINT64 },
696 	{ "getacl",	KSTAT_DATA_UINT64 },
697 	{ "setacl",	KSTAT_DATA_UINT64 },
698 };
699 
700 static void
701 nfsstat_zone_init_aclreq_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
702 {
703 	statsp->aclreqcnt_ptr = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
704 	    "aclreqcnt_v4", aclreqcnt_v4_tmpl, sizeof (aclreqcnt_v4_tmpl));
705 }
706 
707 static void
708 nfsstat_zone_fini_aclreq_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
709 {
710 	nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclreqcnt_v4");
711 	kmem_free(statsp->aclreqcnt_ptr, sizeof (aclreqcnt_v4_tmpl));
712 }
713 
714 /*
715  * NFSv4 server ACL stats
716  */
717 static const kstat_named_t aclproccnt_v4_tmpl[] = {
718 	{ "null",	KSTAT_DATA_UINT64 },
719 	{ "getacl",	KSTAT_DATA_UINT64 },
720 	{ "setacl",	KSTAT_DATA_UINT64 }
721 };
722 
723 kstat_named_t *aclproccnt_v4_ptr;
724 
725 static void
726 nfsstat_zone_init_aclproc_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
727 {
728 	kstat_named_t *ks_data;
729 
730 	ks_data = nfsstat_zone_init_common(zoneid, "nfs_acl", 0,
731 	    "aclproccnt_v4", aclproccnt_v4_tmpl,
732 	    sizeof (aclproccnt_v4_tmpl));
733 	statsp->aclproccnt_ptr = ks_data;
734 	if (zoneid == GLOBAL_ZONEID)
735 		aclproccnt_v4_ptr = ks_data;
736 }
737 
738 static void
739 nfsstat_zone_fini_aclproc_v4(zoneid_t zoneid, struct nfs_version_stats *statsp)
740 {
741 	if (zoneid == GLOBAL_ZONEID)
742 		aclproccnt_v4_ptr = NULL;
743 	nfsstat_zone_fini_common(zoneid, "nfs_acl", 0, "aclproccnt_v4");
744 	kmem_free(statsp->aclproccnt_ptr, sizeof (aclproccnt_v4_tmpl));
745 }
746 
747 /*
748  * Zone initializer callback to setup the kstats.
749  */
750 void *
751 nfsstat_zone_init(zoneid_t zoneid)
752 {
753 	struct nfs_stats *nfs_stats_ptr;
754 
755 	nfs_stats_ptr = kmem_zalloc(sizeof (*nfs_stats_ptr), KM_SLEEP);
756 
757 	/*
758 	 * Initialize all versions of the nfs_server
759 	 */
760 	nfsstat_zone_init_server(zoneid, nfs_stats_ptr->nfs_stats_svstat_ptr);
761 
762 	/*
763 	 * Initialize v2 stats
764 	 */
765 	nfsstat_zone_init_rfsreq_v2(zoneid, &nfs_stats_ptr->nfs_stats_v2);
766 	nfsstat_zone_init_rfsproc_v2(zoneid, &nfs_stats_ptr->nfs_stats_v2);
767 	nfsstat_zone_init_aclreq_v2(zoneid, &nfs_stats_ptr->nfs_stats_v2);
768 	nfsstat_zone_init_aclproc_v2(zoneid, &nfs_stats_ptr->nfs_stats_v2);
769 	/*
770 	 * Initialize v3 stats
771 	 */
772 	nfsstat_zone_init_rfsreq_v3(zoneid, &nfs_stats_ptr->nfs_stats_v3);
773 	nfsstat_zone_init_rfsproc_v3(zoneid, &nfs_stats_ptr->nfs_stats_v3);
774 	nfsstat_zone_init_aclreq_v3(zoneid, &nfs_stats_ptr->nfs_stats_v3);
775 	nfsstat_zone_init_aclproc_v3(zoneid, &nfs_stats_ptr->nfs_stats_v3);
776 	/*
777 	 * Initialize v4 stats
778 	 */
779 	nfsstat_zone_init_rfsreq_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
780 	nfsstat_zone_init_rfsproc_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
781 	nfsstat_zone_init_aclreq_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
782 	nfsstat_zone_init_aclproc_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
783 
784 	return (nfs_stats_ptr);
785 }
786 
787 /*
788  * Zone destructor callback to tear down the various kstats.
789  */
790 void
791 nfsstat_zone_fini(zoneid_t zoneid, void *data)
792 {
793 	struct nfs_stats *nfs_stats_ptr = data;
794 
795 	/*
796 	 * Free nfs:0:nfs_server stats
797 	 */
798 	nfsstat_zone_fini_server(zoneid, nfs_stats_ptr->nfs_stats_svstat_ptr);
799 
800 	/*
801 	 * Free v2 stats
802 	 */
803 	nfsstat_zone_fini_rfsreq_v2(zoneid, &nfs_stats_ptr->nfs_stats_v2);
804 	nfsstat_zone_fini_rfsproc_v2(zoneid, &nfs_stats_ptr->nfs_stats_v2);
805 	nfsstat_zone_fini_aclreq_v2(zoneid, &nfs_stats_ptr->nfs_stats_v2);
806 	nfsstat_zone_fini_aclproc_v2(zoneid, &nfs_stats_ptr->nfs_stats_v2);
807 	/*
808 	 * Free v3 stats
809 	 */
810 	nfsstat_zone_fini_rfsreq_v3(zoneid, &nfs_stats_ptr->nfs_stats_v3);
811 	nfsstat_zone_fini_rfsproc_v3(zoneid, &nfs_stats_ptr->nfs_stats_v3);
812 	nfsstat_zone_fini_aclreq_v3(zoneid, &nfs_stats_ptr->nfs_stats_v3);
813 	nfsstat_zone_fini_aclproc_v3(zoneid, &nfs_stats_ptr->nfs_stats_v3);
814 	/*
815 	 * Free v4 stats
816 	 */
817 	nfsstat_zone_fini_rfsreq_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
818 	nfsstat_zone_fini_rfsproc_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
819 	nfsstat_zone_fini_aclreq_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
820 	nfsstat_zone_fini_aclproc_v4(zoneid, &nfs_stats_ptr->nfs_stats_v4);
821 
822 	kmem_free(nfs_stats_ptr, sizeof (*nfs_stats_ptr));
823 }
824 
825 /*
826  * Support for exp_kstats initialization and tear down
827  */
828 struct exp_kstats *
829 exp_kstats_init(zoneid_t zoneid, int instance, const char *path, size_t len,
830     bool_t pseudo)
831 {
832 	struct exp_kstats *exp_kstats;
833 
834 	exp_kstats = kmem_alloc(sizeof (*exp_kstats), KM_SLEEP);
835 
836 	mutex_init(&exp_kstats->procio_lock, NULL, MUTEX_DEFAULT, NULL);
837 
838 	/*
839 	 * Generic share kstat.
840 	 */
841 	exp_kstats->share_kstat = kstat_create_zone("nfs", instance, "share",
842 	    "misc", KSTAT_TYPE_NAMED,
843 	    sizeof (exp_kstats->share_kstat_data) / sizeof (kstat_named_t),
844 	    KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_VAR_SIZE, zoneid);
845 	if (exp_kstats->share_kstat != NULL) {
846 		len = strnlen(path, len);
847 		exp_kstats->share_path = kmem_alloc(len + 1, KM_SLEEP);
848 		bcopy(path, exp_kstats->share_path, len);
849 		exp_kstats->share_path[len] = '\0';
850 
851 		exp_kstats->share_kstat->ks_data =
852 		    &exp_kstats->share_kstat_data;
853 
854 		kstat_named_init(&exp_kstats->share_kstat_data.path, "path",
855 		    KSTAT_DATA_STRING);
856 		kstat_named_setstr(&exp_kstats->share_kstat_data.path,
857 		    exp_kstats->share_path);
858 
859 		kstat_named_init(&exp_kstats->share_kstat_data.filesystem,
860 		    "filesystem", KSTAT_DATA_STRING);
861 		kstat_named_setstr(&exp_kstats->share_kstat_data.filesystem,
862 		    pseudo ? "pseudo" : "real");
863 
864 		exp_kstats->share_kstat->ks_lock = &exp_kstats->procio_lock;
865 		kstat_install(exp_kstats->share_kstat);
866 	}
867 
868 	/*
869 	 * NFS_ACL version 2
870 	 */
871 	exp_kstats->aclprocio_v2_ptr = rfs_kstat_io_init(zoneid, "nfs_acl",
872 	    instance, "share_v2", "aclprocio_v2", aclproccnt_v2_tmpl,
873 	    ACLPROCCNT_V2_COUNT, &exp_kstats->procio_lock);
874 
875 	/*
876 	 * NFS_ACL version 3
877 	 */
878 	exp_kstats->aclprocio_v3_ptr = rfs_kstat_io_init(zoneid, "nfs_acl",
879 	    instance, "share_v3", "aclprocio_v3", aclproccnt_v3_tmpl,
880 	    ACLPROCCNT_V3_COUNT, &exp_kstats->procio_lock);
881 
882 	/*
883 	 * NFS version 2
884 	 */
885 	exp_kstats->rfsprocio_v2_ptr = rfs_kstat_io_init(zoneid, "nfs",
886 	    instance, "share_v2", "rfsprocio_v2", rfsproccnt_v2_tmpl,
887 	    RFSPROCCNT_V2_COUNT, &exp_kstats->procio_lock);
888 
889 	/*
890 	 * NFS version 3
891 	 */
892 	exp_kstats->rfsprocio_v3_ptr = rfs_kstat_io_init(zoneid, "nfs",
893 	    instance, "share_v3", "rfsprocio_v3", rfsproccnt_v3_tmpl,
894 	    RFSPROCCNT_V3_COUNT, &exp_kstats->procio_lock);
895 
896 	/*
897 	 * NFS version 4
898 	 */
899 	exp_kstats->rfsprocio_v4_ptr = rfs_kstat_io_init(zoneid, "nfs",
900 	    instance, "share_v4", "rfsprocio_v4", rfsproccnt_v4_tmpl,
901 	    RFSPROCCNT_V4_COUNT, &exp_kstats->procio_lock);
902 
903 	return (exp_kstats);
904 }
905 
906 void
907 exp_kstats_delete(struct exp_kstats *exp_kstats)
908 {
909 	if (exp_kstats == NULL)
910 		return;
911 
912 	/*
913 	 * Generic share kstat
914 	 */
915 	if (exp_kstats->share_kstat != NULL) {
916 		kstat_delete(exp_kstats->share_kstat);
917 		exp_kstats->share_kstat = NULL;
918 		strfree(exp_kstats->share_path);
919 	}
920 
921 	/*
922 	 * NFS_ACL kstats
923 	 */
924 	rfs_kstat_io_delete(exp_kstats->aclprocio_v2_ptr, ACLPROCCNT_V2_COUNT);
925 	rfs_kstat_io_delete(exp_kstats->aclprocio_v3_ptr, ACLPROCCNT_V3_COUNT);
926 
927 	/*
928 	 * NFS kstats
929 	 */
930 	rfs_kstat_io_delete(exp_kstats->rfsprocio_v2_ptr, RFSPROCCNT_V2_COUNT);
931 	rfs_kstat_io_delete(exp_kstats->rfsprocio_v3_ptr, RFSPROCCNT_V3_COUNT);
932 	rfs_kstat_io_delete(exp_kstats->rfsprocio_v4_ptr, RFSPROCCNT_V4_COUNT);
933 }
934 
935 void
936 exp_kstats_fini(struct exp_kstats *exp_kstats)
937 {
938 	if (exp_kstats == NULL)
939 		return;
940 
941 	/*
942 	 * Generic share kstat
943 	 */
944 	if (exp_kstats->share_kstat != NULL) {
945 		kstat_delete(exp_kstats->share_kstat);
946 		strfree(exp_kstats->share_path);
947 	}
948 
949 	/*
950 	 * NFS_ACL kstats
951 	 */
952 	rfs_kstat_io_free(exp_kstats->aclprocio_v2_ptr, ACLPROCCNT_V2_COUNT);
953 	rfs_kstat_io_free(exp_kstats->aclprocio_v3_ptr, ACLPROCCNT_V3_COUNT);
954 
955 	/*
956 	 * NFS kstats
957 	 */
958 	rfs_kstat_io_free(exp_kstats->rfsprocio_v2_ptr, RFSPROCCNT_V2_COUNT);
959 	rfs_kstat_io_free(exp_kstats->rfsprocio_v3_ptr, RFSPROCCNT_V3_COUNT);
960 	rfs_kstat_io_free(exp_kstats->rfsprocio_v4_ptr, RFSPROCCNT_V4_COUNT);
961 
962 	mutex_destroy(&exp_kstats->procio_lock);
963 
964 	kmem_free(exp_kstats, sizeof (*exp_kstats));
965 }
966 
967 void
968 exp_kstats_reset(struct exp_kstats *exp_kstats, const char *path, size_t len,
969     bool_t pseudo)
970 {
971 	char *old;
972 	char *new;
973 
974 	if (exp_kstats->share_kstat == NULL)
975 		return;
976 
977 	len = strnlen(path, len);
978 	new = kmem_alloc(len + 1, KM_SLEEP);
979 	bcopy(path, new, len);
980 	new[len] = '\0';
981 
982 	mutex_enter(exp_kstats->share_kstat->ks_lock);
983 	old = exp_kstats->share_path;
984 	exp_kstats->share_path = new;
985 	kstat_named_setstr(&exp_kstats->share_kstat_data.path,
986 	    exp_kstats->share_path);
987 	kstat_named_setstr(&exp_kstats->share_kstat_data.filesystem,
988 	    pseudo ? "pseudo" : "real");
989 	mutex_exit(exp_kstats->share_kstat->ks_lock);
990 
991 	strfree(old);
992 }
993