xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_subr.c (revision 9720e166)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  *  	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
297c478bd9Sstevel@tonic-gate  *	All Rights Reserved
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <sys/param.h>
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/systm.h>
377c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
387c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
397c478bd9Sstevel@tonic-gate #include <sys/session.h>
407c478bd9Sstevel@tonic-gate #include <sys/thread.h>
417c478bd9Sstevel@tonic-gate #include <sys/dnlc.h>
427c478bd9Sstevel@tonic-gate #include <sys/cred.h>
437c478bd9Sstevel@tonic-gate #include <sys/list.h>
447c478bd9Sstevel@tonic-gate #include <sys/sdt.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #include <rpc/types.h>
477c478bd9Sstevel@tonic-gate #include <rpc/xdr.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include <nfs/nfs4.h>
547c478bd9Sstevel@tonic-gate #include <nfs/rnode4.h>
557c478bd9Sstevel@tonic-gate #include <nfs/nfs4_clnt.h>
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate  * client side statistics
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate static const struct clstat4 clstat4_tmpl = {
617c478bd9Sstevel@tonic-gate 	{ "calls",	KSTAT_DATA_UINT64 },
627c478bd9Sstevel@tonic-gate 	{ "badcalls",	KSTAT_DATA_UINT64 },
637c478bd9Sstevel@tonic-gate 	{ "clgets",	KSTAT_DATA_UINT64 },
647c478bd9Sstevel@tonic-gate 	{ "cltoomany",	KSTAT_DATA_UINT64 },
657c478bd9Sstevel@tonic-gate #ifdef DEBUG
667c478bd9Sstevel@tonic-gate 	{ "clalloc",	KSTAT_DATA_UINT64 },
677c478bd9Sstevel@tonic-gate 	{ "noresponse",	KSTAT_DATA_UINT64 },
687c478bd9Sstevel@tonic-gate 	{ "failover",	KSTAT_DATA_UINT64 },
697c478bd9Sstevel@tonic-gate 	{ "remap",	KSTAT_DATA_UINT64 },
707c478bd9Sstevel@tonic-gate #endif
717c478bd9Sstevel@tonic-gate };
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate #ifdef DEBUG
747c478bd9Sstevel@tonic-gate struct clstat4_debug clstat4_debug = {
757c478bd9Sstevel@tonic-gate 	{ "nrnode",	KSTAT_DATA_UINT64 },
767c478bd9Sstevel@tonic-gate 	{ "access",	KSTAT_DATA_UINT64 },
777c478bd9Sstevel@tonic-gate 	{ "dirent",	KSTAT_DATA_UINT64 },
787c478bd9Sstevel@tonic-gate 	{ "dirents",	KSTAT_DATA_UINT64 },
797c478bd9Sstevel@tonic-gate 	{ "reclaim",	KSTAT_DATA_UINT64 },
807c478bd9Sstevel@tonic-gate 	{ "clreclaim",	KSTAT_DATA_UINT64 },
817c478bd9Sstevel@tonic-gate 	{ "f_reclaim",	KSTAT_DATA_UINT64 },
827c478bd9Sstevel@tonic-gate 	{ "a_reclaim",	KSTAT_DATA_UINT64 },
837c478bd9Sstevel@tonic-gate 	{ "r_reclaim",	KSTAT_DATA_UINT64 },
847c478bd9Sstevel@tonic-gate 	{ "r_path",	KSTAT_DATA_UINT64 },
857c478bd9Sstevel@tonic-gate };
867c478bd9Sstevel@tonic-gate #endif
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate /*
897c478bd9Sstevel@tonic-gate  * We keep a global list of per-zone client data, so we can clean up all zones
907c478bd9Sstevel@tonic-gate  * if we get low on memory.
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate static list_t nfs4_clnt_list;
937c478bd9Sstevel@tonic-gate static kmutex_t nfs4_clnt_list_lock;
947c478bd9Sstevel@tonic-gate static zone_key_t nfs4clnt_zone_key;
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate static struct kmem_cache *chtab4_cache;
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate #ifdef DEBUG
997c478bd9Sstevel@tonic-gate static int nfs4_rfscall_debug;
1007c478bd9Sstevel@tonic-gate static int nfs4_try_failover_any;
1017c478bd9Sstevel@tonic-gate int nfs4_utf8_debug = 0;
1027c478bd9Sstevel@tonic-gate #endif
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate  * NFSv4 readdir cache implementation
1067c478bd9Sstevel@tonic-gate  */
1077c478bd9Sstevel@tonic-gate typedef struct rddir4_cache_impl {
1087c478bd9Sstevel@tonic-gate 	rddir4_cache	rc;		/* readdir cache element */
1097c478bd9Sstevel@tonic-gate 	kmutex_t	lock;		/* lock protects count */
1107c478bd9Sstevel@tonic-gate 	uint_t		count;		/* reference count */
1117c478bd9Sstevel@tonic-gate 	avl_node_t	tree;		/* AVL tree link */
1127c478bd9Sstevel@tonic-gate } rddir4_cache_impl;
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate static int rddir4_cache_compar(const void *, const void *);
1157c478bd9Sstevel@tonic-gate static void rddir4_cache_free(rddir4_cache_impl *);
1167c478bd9Sstevel@tonic-gate static rddir4_cache *rddir4_cache_alloc(int);
1177c478bd9Sstevel@tonic-gate static void rddir4_cache_hold(rddir4_cache *);
1187c478bd9Sstevel@tonic-gate static int try_failover(enum clnt_stat);
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate static int nfs4_readdir_cache_hits = 0;
1217c478bd9Sstevel@tonic-gate static int nfs4_readdir_cache_waits = 0;
1227c478bd9Sstevel@tonic-gate static int nfs4_readdir_cache_misses = 0;
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate /*
1257c478bd9Sstevel@tonic-gate  * Shared nfs4 functions
1267c478bd9Sstevel@tonic-gate  */
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate /*
1297c478bd9Sstevel@tonic-gate  * Copy an nfs_fh4.  The destination storage (to->nfs_fh4_val) must already
1307c478bd9Sstevel@tonic-gate  * be allocated.
1317c478bd9Sstevel@tonic-gate  */
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate void
1347c478bd9Sstevel@tonic-gate nfs_fh4_copy(nfs_fh4 *from, nfs_fh4 *to)
1357c478bd9Sstevel@tonic-gate {
1367c478bd9Sstevel@tonic-gate 	to->nfs_fh4_len = from->nfs_fh4_len;
1377c478bd9Sstevel@tonic-gate 	bcopy(from->nfs_fh4_val, to->nfs_fh4_val, to->nfs_fh4_len);
1387c478bd9Sstevel@tonic-gate }
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate /*
1417c478bd9Sstevel@tonic-gate  * nfs4cmpfh - compare 2 filehandles.
1427c478bd9Sstevel@tonic-gate  * Returns 0 if the two nfsv4 filehandles are the same, -1 if the first is
1437c478bd9Sstevel@tonic-gate  * "less" than the second, +1 if the first is "greater" than the second.
1447c478bd9Sstevel@tonic-gate  */
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate int
1477c478bd9Sstevel@tonic-gate nfs4cmpfh(const nfs_fh4 *fh4p1, const nfs_fh4 *fh4p2)
1487c478bd9Sstevel@tonic-gate {
1497c478bd9Sstevel@tonic-gate 	const char *c1, *c2;
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	if (fh4p1->nfs_fh4_len < fh4p2->nfs_fh4_len)
1527c478bd9Sstevel@tonic-gate 		return (-1);
1537c478bd9Sstevel@tonic-gate 	if (fh4p1->nfs_fh4_len > fh4p2->nfs_fh4_len)
1547c478bd9Sstevel@tonic-gate 		return (1);
1557c478bd9Sstevel@tonic-gate 	for (c1 = fh4p1->nfs_fh4_val, c2 = fh4p2->nfs_fh4_val;
1567c478bd9Sstevel@tonic-gate 	    c1 < fh4p1->nfs_fh4_val + fh4p1->nfs_fh4_len;
1577c478bd9Sstevel@tonic-gate 	    c1++, c2++) {
1587c478bd9Sstevel@tonic-gate 		if (*c1 < *c2)
1597c478bd9Sstevel@tonic-gate 			return (-1);
1607c478bd9Sstevel@tonic-gate 		if (*c1 > *c2)
1617c478bd9Sstevel@tonic-gate 			return (1);
1627c478bd9Sstevel@tonic-gate 	}
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	return (0);
1657c478bd9Sstevel@tonic-gate }
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate /*
1687c478bd9Sstevel@tonic-gate  * Compare two v4 filehandles.  Return zero if they're the same, non-zero
1697c478bd9Sstevel@tonic-gate  * if they're not.  Like nfs4cmpfh(), but different filehandle
1707c478bd9Sstevel@tonic-gate  * representation, and doesn't provide information about greater than or
1717c478bd9Sstevel@tonic-gate  * less than.
1727c478bd9Sstevel@tonic-gate  */
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate int
1757c478bd9Sstevel@tonic-gate nfs4cmpfhandle(nfs4_fhandle_t *fh1, nfs4_fhandle_t *fh2)
1767c478bd9Sstevel@tonic-gate {
1777c478bd9Sstevel@tonic-gate 	if (fh1->fh_len == fh2->fh_len)
1787c478bd9Sstevel@tonic-gate 		return (bcmp(fh1->fh_buf, fh2->fh_buf, fh1->fh_len));
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	return (1);
1817c478bd9Sstevel@tonic-gate }
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate int
1847c478bd9Sstevel@tonic-gate stateid4_cmp(stateid4 *s1, stateid4 *s2)
1857c478bd9Sstevel@tonic-gate {
1867c478bd9Sstevel@tonic-gate 	if (bcmp(s1, s2, sizeof (stateid4)) == 0)
1877c478bd9Sstevel@tonic-gate 		return (1);
1887c478bd9Sstevel@tonic-gate 	else
1897c478bd9Sstevel@tonic-gate 		return (0);
1907c478bd9Sstevel@tonic-gate }
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate nfsstat4
1937c478bd9Sstevel@tonic-gate puterrno4(int error)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate 	switch (error) {
1967c478bd9Sstevel@tonic-gate 	case 0:
1977c478bd9Sstevel@tonic-gate 		return (NFS4_OK);
1987c478bd9Sstevel@tonic-gate 	case EPERM:
1997c478bd9Sstevel@tonic-gate 		return (NFS4ERR_PERM);
2007c478bd9Sstevel@tonic-gate 	case ENOENT:
2017c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOENT);
2027c478bd9Sstevel@tonic-gate 	case EINTR:
2037c478bd9Sstevel@tonic-gate 		return (NFS4ERR_IO);
2047c478bd9Sstevel@tonic-gate 	case EIO:
2057c478bd9Sstevel@tonic-gate 		return (NFS4ERR_IO);
2067c478bd9Sstevel@tonic-gate 	case ENXIO:
2077c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NXIO);
2087c478bd9Sstevel@tonic-gate 	case ENOMEM:
2097c478bd9Sstevel@tonic-gate 		return (NFS4ERR_RESOURCE);
2107c478bd9Sstevel@tonic-gate 	case EACCES:
2117c478bd9Sstevel@tonic-gate 		return (NFS4ERR_ACCESS);
2127c478bd9Sstevel@tonic-gate 	case EBUSY:
2137c478bd9Sstevel@tonic-gate 		return (NFS4ERR_IO);
2147c478bd9Sstevel@tonic-gate 	case EEXIST:
2157c478bd9Sstevel@tonic-gate 		return (NFS4ERR_EXIST);
2167c478bd9Sstevel@tonic-gate 	case EXDEV:
2177c478bd9Sstevel@tonic-gate 		return (NFS4ERR_XDEV);
2187c478bd9Sstevel@tonic-gate 	case ENODEV:
2197c478bd9Sstevel@tonic-gate 		return (NFS4ERR_IO);
2207c478bd9Sstevel@tonic-gate 	case ENOTDIR:
2217c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOTDIR);
2227c478bd9Sstevel@tonic-gate 	case EISDIR:
2237c478bd9Sstevel@tonic-gate 		return (NFS4ERR_ISDIR);
2247c478bd9Sstevel@tonic-gate 	case EINVAL:
2257c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
2267c478bd9Sstevel@tonic-gate 	case EMFILE:
2277c478bd9Sstevel@tonic-gate 		return (NFS4ERR_RESOURCE);
2287c478bd9Sstevel@tonic-gate 	case EFBIG:
2297c478bd9Sstevel@tonic-gate 		return (NFS4ERR_FBIG);
2307c478bd9Sstevel@tonic-gate 	case ENOSPC:
2317c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOSPC);
2327c478bd9Sstevel@tonic-gate 	case EROFS:
2337c478bd9Sstevel@tonic-gate 		return (NFS4ERR_ROFS);
2347c478bd9Sstevel@tonic-gate 	case EMLINK:
2357c478bd9Sstevel@tonic-gate 		return (NFS4ERR_MLINK);
2367c478bd9Sstevel@tonic-gate 	case EDEADLK:
2377c478bd9Sstevel@tonic-gate 		return (NFS4ERR_DEADLOCK);
2387c478bd9Sstevel@tonic-gate 	case ENOLCK:
2397c478bd9Sstevel@tonic-gate 		return (NFS4ERR_DENIED);
2407c478bd9Sstevel@tonic-gate 	case EREMOTE:
2417c478bd9Sstevel@tonic-gate 		return (NFS4ERR_SERVERFAULT);
2427c478bd9Sstevel@tonic-gate 	case ENOTSUP:
2437c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOTSUPP);
2447c478bd9Sstevel@tonic-gate 	case EDQUOT:
2457c478bd9Sstevel@tonic-gate 		return (NFS4ERR_DQUOT);
2467c478bd9Sstevel@tonic-gate 	case ENAMETOOLONG:
2477c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NAMETOOLONG);
2487c478bd9Sstevel@tonic-gate 	case EOVERFLOW:
2497c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
2507c478bd9Sstevel@tonic-gate 	case ENOSYS:
2517c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOTSUPP);
2527c478bd9Sstevel@tonic-gate 	case ENOTEMPTY:
2537c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOTEMPTY);
2547c478bd9Sstevel@tonic-gate 	case EOPNOTSUPP:
2557c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOTSUPP);
2567c478bd9Sstevel@tonic-gate 	case ESTALE:
2577c478bd9Sstevel@tonic-gate 		return (NFS4ERR_STALE);
2587c478bd9Sstevel@tonic-gate 	case EAGAIN:
2597c478bd9Sstevel@tonic-gate 		if (curthread->t_flag & T_WOULDBLOCK) {
2607c478bd9Sstevel@tonic-gate 			curthread->t_flag &= ~T_WOULDBLOCK;
2617c478bd9Sstevel@tonic-gate 			return (NFS4ERR_DELAY);
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 		return (NFS4ERR_LOCKED);
2647c478bd9Sstevel@tonic-gate 	default:
2657c478bd9Sstevel@tonic-gate 		return ((enum nfsstat4)error);
2667c478bd9Sstevel@tonic-gate 	}
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate int
2707c478bd9Sstevel@tonic-gate geterrno4(enum nfsstat4 status)
2717c478bd9Sstevel@tonic-gate {
2727c478bd9Sstevel@tonic-gate 	switch (status) {
2737c478bd9Sstevel@tonic-gate 	case NFS4_OK:
2747c478bd9Sstevel@tonic-gate 		return (0);
2757c478bd9Sstevel@tonic-gate 	case NFS4ERR_PERM:
2767c478bd9Sstevel@tonic-gate 		return (EPERM);
2777c478bd9Sstevel@tonic-gate 	case NFS4ERR_NOENT:
2787c478bd9Sstevel@tonic-gate 		return (ENOENT);
2797c478bd9Sstevel@tonic-gate 	case NFS4ERR_IO:
2807c478bd9Sstevel@tonic-gate 		return (EIO);
2817c478bd9Sstevel@tonic-gate 	case NFS4ERR_NXIO:
2827c478bd9Sstevel@tonic-gate 		return (ENXIO);
2837c478bd9Sstevel@tonic-gate 	case NFS4ERR_ACCESS:
2847c478bd9Sstevel@tonic-gate 		return (EACCES);
2857c478bd9Sstevel@tonic-gate 	case NFS4ERR_EXIST:
2867c478bd9Sstevel@tonic-gate 		return (EEXIST);
2877c478bd9Sstevel@tonic-gate 	case NFS4ERR_XDEV:
2887c478bd9Sstevel@tonic-gate 		return (EXDEV);
2897c478bd9Sstevel@tonic-gate 	case NFS4ERR_NOTDIR:
2907c478bd9Sstevel@tonic-gate 		return (ENOTDIR);
2917c478bd9Sstevel@tonic-gate 	case NFS4ERR_ISDIR:
2927c478bd9Sstevel@tonic-gate 		return (EISDIR);
2937c478bd9Sstevel@tonic-gate 	case NFS4ERR_INVAL:
2947c478bd9Sstevel@tonic-gate 		return (EINVAL);
2957c478bd9Sstevel@tonic-gate 	case NFS4ERR_FBIG:
2967c478bd9Sstevel@tonic-gate 		return (EFBIG);
2977c478bd9Sstevel@tonic-gate 	case NFS4ERR_NOSPC:
2987c478bd9Sstevel@tonic-gate 		return (ENOSPC);
2997c478bd9Sstevel@tonic-gate 	case NFS4ERR_ROFS:
3007c478bd9Sstevel@tonic-gate 		return (EROFS);
3017c478bd9Sstevel@tonic-gate 	case NFS4ERR_MLINK:
3027c478bd9Sstevel@tonic-gate 		return (EMLINK);
3037c478bd9Sstevel@tonic-gate 	case NFS4ERR_NAMETOOLONG:
3047c478bd9Sstevel@tonic-gate 		return (ENAMETOOLONG);
3057c478bd9Sstevel@tonic-gate 	case NFS4ERR_NOTEMPTY:
3067c478bd9Sstevel@tonic-gate 		return (ENOTEMPTY);
3077c478bd9Sstevel@tonic-gate 	case NFS4ERR_DQUOT:
3087c478bd9Sstevel@tonic-gate 		return (EDQUOT);
3097c478bd9Sstevel@tonic-gate 	case NFS4ERR_STALE:
3107c478bd9Sstevel@tonic-gate 		return (ESTALE);
3117c478bd9Sstevel@tonic-gate 	case NFS4ERR_BADHANDLE:
3127c478bd9Sstevel@tonic-gate 		return (ESTALE);
3137c478bd9Sstevel@tonic-gate 	case NFS4ERR_BAD_COOKIE:
3147c478bd9Sstevel@tonic-gate 		return (EINVAL);
3157c478bd9Sstevel@tonic-gate 	case NFS4ERR_NOTSUPP:
3167c478bd9Sstevel@tonic-gate 		return (EOPNOTSUPP);
3177c478bd9Sstevel@tonic-gate 	case NFS4ERR_TOOSMALL:
3187c478bd9Sstevel@tonic-gate 		return (EINVAL);
3197c478bd9Sstevel@tonic-gate 	case NFS4ERR_SERVERFAULT:
3207c478bd9Sstevel@tonic-gate 		return (EIO);
3217c478bd9Sstevel@tonic-gate 	case NFS4ERR_BADTYPE:
3227c478bd9Sstevel@tonic-gate 		return (EINVAL);
3237c478bd9Sstevel@tonic-gate 	case NFS4ERR_DELAY:
3247c478bd9Sstevel@tonic-gate 		return (ENXIO);
3257c478bd9Sstevel@tonic-gate 	case NFS4ERR_SAME:
3267c478bd9Sstevel@tonic-gate 		return (EPROTO);
3277c478bd9Sstevel@tonic-gate 	case NFS4ERR_DENIED:
3287c478bd9Sstevel@tonic-gate 		return (ENOLCK);
3297c478bd9Sstevel@tonic-gate 	case NFS4ERR_EXPIRED:
3307c478bd9Sstevel@tonic-gate 		return (EPROTO);
3317c478bd9Sstevel@tonic-gate 	case NFS4ERR_LOCKED:
3327c478bd9Sstevel@tonic-gate 		return (EACCES);
3337c478bd9Sstevel@tonic-gate 	case NFS4ERR_GRACE:
3347c478bd9Sstevel@tonic-gate 		return (EAGAIN);
3357c478bd9Sstevel@tonic-gate 	case NFS4ERR_FHEXPIRED:	/* if got here, failed to get a new fh */
3367c478bd9Sstevel@tonic-gate 		return (ESTALE);
3377c478bd9Sstevel@tonic-gate 	case NFS4ERR_SHARE_DENIED:
3387c478bd9Sstevel@tonic-gate 		return (EACCES);
3397c478bd9Sstevel@tonic-gate 	case NFS4ERR_WRONGSEC:
3407c478bd9Sstevel@tonic-gate 		return (EPERM);
3417c478bd9Sstevel@tonic-gate 	case NFS4ERR_CLID_INUSE:
3427c478bd9Sstevel@tonic-gate 		return (EAGAIN);
3437c478bd9Sstevel@tonic-gate 	case NFS4ERR_RESOURCE:
3447c478bd9Sstevel@tonic-gate 		return (EAGAIN);
3457c478bd9Sstevel@tonic-gate 	case NFS4ERR_MOVED:
3467c478bd9Sstevel@tonic-gate 		return (EPROTO);
3477c478bd9Sstevel@tonic-gate 	case NFS4ERR_NOFILEHANDLE:
3487c478bd9Sstevel@tonic-gate 		return (EIO);
3497c478bd9Sstevel@tonic-gate 	case NFS4ERR_MINOR_VERS_MISMATCH:
3507c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3517c478bd9Sstevel@tonic-gate 	case NFS4ERR_STALE_CLIENTID:
3527c478bd9Sstevel@tonic-gate 		return (EIO);
3537c478bd9Sstevel@tonic-gate 	case NFS4ERR_STALE_STATEID:
3547c478bd9Sstevel@tonic-gate 		return (EIO);
3557c478bd9Sstevel@tonic-gate 	case NFS4ERR_OLD_STATEID:
3567c478bd9Sstevel@tonic-gate 		return (EIO);
3577c478bd9Sstevel@tonic-gate 	case NFS4ERR_BAD_STATEID:
3587c478bd9Sstevel@tonic-gate 		return (EIO);
3597c478bd9Sstevel@tonic-gate 	case NFS4ERR_BAD_SEQID:
3607c478bd9Sstevel@tonic-gate 		return (EIO);
3617c478bd9Sstevel@tonic-gate 	case NFS4ERR_NOT_SAME:
3627c478bd9Sstevel@tonic-gate 		return (EPROTO);
3637c478bd9Sstevel@tonic-gate 	case NFS4ERR_LOCK_RANGE:
3647c478bd9Sstevel@tonic-gate 		return (EPROTO);
3657c478bd9Sstevel@tonic-gate 	case NFS4ERR_SYMLINK:
3667c478bd9Sstevel@tonic-gate 		return (EPROTO);
3677c478bd9Sstevel@tonic-gate 	case NFS4ERR_RESTOREFH:
3687c478bd9Sstevel@tonic-gate 		return (EPROTO);
3697c478bd9Sstevel@tonic-gate 	case NFS4ERR_LEASE_MOVED:
3707c478bd9Sstevel@tonic-gate 		return (EPROTO);
3717c478bd9Sstevel@tonic-gate 	case NFS4ERR_ATTRNOTSUPP:
3727c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3737c478bd9Sstevel@tonic-gate 	case NFS4ERR_NO_GRACE:
3747c478bd9Sstevel@tonic-gate 		return (EPROTO);
3757c478bd9Sstevel@tonic-gate 	case NFS4ERR_RECLAIM_BAD:
3767c478bd9Sstevel@tonic-gate 		return (EPROTO);
3777c478bd9Sstevel@tonic-gate 	case NFS4ERR_RECLAIM_CONFLICT:
3787c478bd9Sstevel@tonic-gate 		return (EPROTO);
3797c478bd9Sstevel@tonic-gate 	case NFS4ERR_BADXDR:
3807c478bd9Sstevel@tonic-gate 		return (EINVAL);
3817c478bd9Sstevel@tonic-gate 	case NFS4ERR_LOCKS_HELD:
3827c478bd9Sstevel@tonic-gate 		return (EIO);
3837c478bd9Sstevel@tonic-gate 	case NFS4ERR_OPENMODE:
3847c478bd9Sstevel@tonic-gate 		return (EACCES);
3857c478bd9Sstevel@tonic-gate 	case NFS4ERR_BADOWNER:
3867c478bd9Sstevel@tonic-gate 		/*
3877c478bd9Sstevel@tonic-gate 		 * Client and server are in different DNS domains
3887c478bd9Sstevel@tonic-gate 		 * and the NFSMAPID_DOMAIN in /etc/default/nfs
3897c478bd9Sstevel@tonic-gate 		 * doesn't match.  No good answer here.  Return
3907c478bd9Sstevel@tonic-gate 		 * EACCESS, which translates to "permission denied".
3917c478bd9Sstevel@tonic-gate 		 */
3927c478bd9Sstevel@tonic-gate 		return (EACCES);
3937c478bd9Sstevel@tonic-gate 	case NFS4ERR_BADCHAR:
3947c478bd9Sstevel@tonic-gate 		return (EINVAL);
3957c478bd9Sstevel@tonic-gate 	case NFS4ERR_BADNAME:
3967c478bd9Sstevel@tonic-gate 		return (EINVAL);
3977c478bd9Sstevel@tonic-gate 	case NFS4ERR_BAD_RANGE:
3987c478bd9Sstevel@tonic-gate 		return (EIO);
3997c478bd9Sstevel@tonic-gate 	case NFS4ERR_LOCK_NOTSUPP:
4007c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
4017c478bd9Sstevel@tonic-gate 	case NFS4ERR_OP_ILLEGAL:
4027c478bd9Sstevel@tonic-gate 		return (EINVAL);
4037c478bd9Sstevel@tonic-gate 	case NFS4ERR_DEADLOCK:
4047c478bd9Sstevel@tonic-gate 		return (EDEADLK);
4057c478bd9Sstevel@tonic-gate 	case NFS4ERR_FILE_OPEN:
4067c478bd9Sstevel@tonic-gate 		return (EACCES);
4077c478bd9Sstevel@tonic-gate 	case NFS4ERR_ADMIN_REVOKED:
4087c478bd9Sstevel@tonic-gate 		return (EPROTO);
4097c478bd9Sstevel@tonic-gate 	case NFS4ERR_CB_PATH_DOWN:
4107c478bd9Sstevel@tonic-gate 		return (EPROTO);
4117c478bd9Sstevel@tonic-gate 	default:
4127c478bd9Sstevel@tonic-gate #ifdef DEBUG
4137c478bd9Sstevel@tonic-gate 		zcmn_err(getzoneid(), CE_WARN, "geterrno4: got status %d",
4147c478bd9Sstevel@tonic-gate 		    status);
4157c478bd9Sstevel@tonic-gate #endif
4167c478bd9Sstevel@tonic-gate 		return ((int)status);
4177c478bd9Sstevel@tonic-gate 	}
4187c478bd9Sstevel@tonic-gate }
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate void
4217c478bd9Sstevel@tonic-gate nfs4_log_badowner(mntinfo4_t *mi, nfs_opnum4 op)
4227c478bd9Sstevel@tonic-gate {
4237c478bd9Sstevel@tonic-gate 	nfs4_server_t *server;
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	/*
4267c478bd9Sstevel@tonic-gate 	 * Return if already printed/queued a msg
4277c478bd9Sstevel@tonic-gate 	 * for this mount point.
4287c478bd9Sstevel@tonic-gate 	 */
4297c478bd9Sstevel@tonic-gate 	if (mi->mi_flags & MI4_BADOWNER_DEBUG)
4307c478bd9Sstevel@tonic-gate 		return;
4317c478bd9Sstevel@tonic-gate 	/*
4327c478bd9Sstevel@tonic-gate 	 * Happens once per client <-> server pair.
4337c478bd9Sstevel@tonic-gate 	 */
4347c478bd9Sstevel@tonic-gate 	if (nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER,
4357c478bd9Sstevel@tonic-gate 		mi->mi_flags & MI4_INT))
4367c478bd9Sstevel@tonic-gate 		return;
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	server = find_nfs4_server(mi);
4397c478bd9Sstevel@tonic-gate 	if (server == NULL) {
4407c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&mi->mi_recovlock);
4417c478bd9Sstevel@tonic-gate 		return;
4427c478bd9Sstevel@tonic-gate 	}
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	if (!(server->s_flags & N4S_BADOWNER_DEBUG)) {
4457c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_WARN,
4467c478bd9Sstevel@tonic-gate 		    "!NFSMAPID_DOMAIN does not match"
4477c478bd9Sstevel@tonic-gate 		    " the server: %s domain.\n"
4487c478bd9Sstevel@tonic-gate 		    "Please check configuration",
4497c478bd9Sstevel@tonic-gate 		    mi->mi_curr_serv->sv_hostname);
4507c478bd9Sstevel@tonic-gate 		server->s_flags |= N4S_BADOWNER_DEBUG;
4517c478bd9Sstevel@tonic-gate 	}
4527c478bd9Sstevel@tonic-gate 	mutex_exit(&server->s_lock);
4537c478bd9Sstevel@tonic-gate 	nfs4_server_rele(server);
4547c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&mi->mi_recovlock);
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	/*
4577c478bd9Sstevel@tonic-gate 	 * Happens once per mntinfo4_t.
4587c478bd9Sstevel@tonic-gate 	 * This error is deemed as one of the recovery facts "RF_BADOWNER",
4597c478bd9Sstevel@tonic-gate 	 * queue this in the mesg queue for this mount_info. This message
4607c478bd9Sstevel@tonic-gate 	 * is not printed, meaning its absent from id_to_dump_solo_fact()
4617c478bd9Sstevel@tonic-gate 	 * but its there for inspection if the queue is ever dumped/inspected.
4627c478bd9Sstevel@tonic-gate 	 */
4637c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
4647c478bd9Sstevel@tonic-gate 	if (!(mi->mi_flags & MI4_BADOWNER_DEBUG)) {
4657c478bd9Sstevel@tonic-gate 		nfs4_queue_fact(RF_BADOWNER, mi, NFS4ERR_BADOWNER, 0, op,
4667c478bd9Sstevel@tonic-gate 		    FALSE, NULL, 0, NULL);
4677c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_BADOWNER_DEBUG;
4687c478bd9Sstevel@tonic-gate 	}
4697c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
4707c478bd9Sstevel@tonic-gate }
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate int
4757c478bd9Sstevel@tonic-gate nfs4_time_ntov(nfstime4 *ntime, timestruc_t *vatime)
4767c478bd9Sstevel@tonic-gate {
4777c478bd9Sstevel@tonic-gate 	int64_t sec;
4787c478bd9Sstevel@tonic-gate 	int32_t nsec;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	/*
4817c478bd9Sstevel@tonic-gate 	 * Here check that the nfsv4 time is valid for the system.
4827c478bd9Sstevel@tonic-gate 	 * nfsv4 time value is a signed 64-bit, and the system time
4837c478bd9Sstevel@tonic-gate 	 * may be either int64_t or int32_t (depends on the kernel),
4847c478bd9Sstevel@tonic-gate 	 * so if the kernel is 32-bit, the nfsv4 time value may not fit.
4857c478bd9Sstevel@tonic-gate 	 */
4867c478bd9Sstevel@tonic-gate #ifndef _LP64
4877c478bd9Sstevel@tonic-gate 	if (! NFS4_TIME_OK(ntime->seconds)) {
4887c478bd9Sstevel@tonic-gate 		return (EOVERFLOW);
4897c478bd9Sstevel@tonic-gate 	}
4907c478bd9Sstevel@tonic-gate #endif
4917c478bd9Sstevel@tonic-gate 
492*9720e166Sjasmith 	/* Invalid to specify 1 billion (or more) nsecs */
493*9720e166Sjasmith 	if (ntime->nseconds >= 1000000000)
494*9720e166Sjasmith 		return (EINVAL);
495*9720e166Sjasmith 
4967c478bd9Sstevel@tonic-gate 	if (ntime->seconds < 0) {
4977c478bd9Sstevel@tonic-gate 		sec = ntime->seconds + 1;
4987c478bd9Sstevel@tonic-gate 		nsec = -1000000000 + ntime->nseconds;
4997c478bd9Sstevel@tonic-gate 	} else {
5007c478bd9Sstevel@tonic-gate 		sec = ntime->seconds;
5017c478bd9Sstevel@tonic-gate 		nsec = ntime->nseconds;
5027c478bd9Sstevel@tonic-gate 	}
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	vatime->tv_sec = sec;
5057c478bd9Sstevel@tonic-gate 	vatime->tv_nsec = nsec;
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	return (0);
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate int
5117c478bd9Sstevel@tonic-gate nfs4_time_vton(timestruc_t *vatime, nfstime4 *ntime)
5127c478bd9Sstevel@tonic-gate {
5137c478bd9Sstevel@tonic-gate 	int64_t sec;
5147c478bd9Sstevel@tonic-gate 	uint32_t nsec;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	/*
5177c478bd9Sstevel@tonic-gate 	 * nfsv4 time value is a signed 64-bit, and the system time
5187c478bd9Sstevel@tonic-gate 	 * may be either int64_t or int32_t (depends on the kernel),
5197c478bd9Sstevel@tonic-gate 	 * so all system time values will fit.
5207c478bd9Sstevel@tonic-gate 	 */
5217c478bd9Sstevel@tonic-gate 	if (vatime->tv_nsec >= 0) {
5227c478bd9Sstevel@tonic-gate 		sec = vatime->tv_sec;
5237c478bd9Sstevel@tonic-gate 		nsec = vatime->tv_nsec;
5247c478bd9Sstevel@tonic-gate 	} else {
5257c478bd9Sstevel@tonic-gate 		sec = vatime->tv_sec - 1;
5267c478bd9Sstevel@tonic-gate 		nsec = 1000000000 + vatime->tv_nsec;
5277c478bd9Sstevel@tonic-gate 	}
5287c478bd9Sstevel@tonic-gate 	ntime->seconds = sec;
5297c478bd9Sstevel@tonic-gate 	ntime->nseconds = nsec;
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	return (0);
5327c478bd9Sstevel@tonic-gate }
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate /*
5357c478bd9Sstevel@tonic-gate  * Converts a utf8 string to a valid null terminated filename string.
5367c478bd9Sstevel@tonic-gate  *
5377c478bd9Sstevel@tonic-gate  * XXX - Not actually translating the UTF-8 string as per RFC 2279.
5387c478bd9Sstevel@tonic-gate  *	 For now, just validate that the UTF-8 string off the wire
5397c478bd9Sstevel@tonic-gate  *	 does not have characters that will freak out UFS, and leave
5407c478bd9Sstevel@tonic-gate  *	 it at that.
5417c478bd9Sstevel@tonic-gate  */
5427c478bd9Sstevel@tonic-gate char *
5437c478bd9Sstevel@tonic-gate utf8_to_fn(utf8string *u8s, uint_t *lenp, char *s)
5447c478bd9Sstevel@tonic-gate {
5457c478bd9Sstevel@tonic-gate 	ASSERT(lenp != NULL);
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	if (u8s == NULL || u8s->utf8string_len <= 0 ||
5487c478bd9Sstevel@tonic-gate 					u8s->utf8string_val == NULL)
5497c478bd9Sstevel@tonic-gate 		return (NULL);
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	/*
5527c478bd9Sstevel@tonic-gate 	 * Check for obvious illegal filename chars
5537c478bd9Sstevel@tonic-gate 	 */
5547c478bd9Sstevel@tonic-gate 	if (utf8_strchr(u8s, '/') != NULL) {
5557c478bd9Sstevel@tonic-gate #ifdef DEBUG
5567c478bd9Sstevel@tonic-gate 		if (nfs4_utf8_debug) {
5577c478bd9Sstevel@tonic-gate 			char *path;
5587c478bd9Sstevel@tonic-gate 			int len = u8s->utf8string_len;
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 			path = kmem_alloc(len + 1, KM_SLEEP);
5617c478bd9Sstevel@tonic-gate 			bcopy(u8s->utf8string_val, path, len);
5627c478bd9Sstevel@tonic-gate 			path[len] = '\0';
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 			zcmn_err(getzoneid(), CE_WARN,
5657c478bd9Sstevel@tonic-gate 			    "Invalid UTF-8 filename: %s", path);
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 			kmem_free(path, len + 1);
5687c478bd9Sstevel@tonic-gate 		}
5697c478bd9Sstevel@tonic-gate #endif
5707c478bd9Sstevel@tonic-gate 		return (NULL);
5717c478bd9Sstevel@tonic-gate 	}
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	return (utf8_to_str(u8s, lenp, s));
5747c478bd9Sstevel@tonic-gate }
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate /*
5777c478bd9Sstevel@tonic-gate  * Converts a utf8 string to a C string.
5787c478bd9Sstevel@tonic-gate  * kmem_allocs a new string if not supplied
5797c478bd9Sstevel@tonic-gate  */
5807c478bd9Sstevel@tonic-gate char *
5817c478bd9Sstevel@tonic-gate utf8_to_str(utf8string *str, uint_t *lenp, char *s)
5827c478bd9Sstevel@tonic-gate {
5837c478bd9Sstevel@tonic-gate 	char	*sp;
5847c478bd9Sstevel@tonic-gate 	char	*u8p;
5857c478bd9Sstevel@tonic-gate 	int	len;
5867c478bd9Sstevel@tonic-gate 	int	 i;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	ASSERT(lenp != NULL);
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 	if (str == NULL)
5917c478bd9Sstevel@tonic-gate 		return (NULL);
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	u8p = str->utf8string_val;
5947c478bd9Sstevel@tonic-gate 	len = str->utf8string_len;
5957c478bd9Sstevel@tonic-gate 	if (len <= 0 || u8p == NULL) {
5967c478bd9Sstevel@tonic-gate 		if (s)
5977c478bd9Sstevel@tonic-gate 			*s = '\0';
5987c478bd9Sstevel@tonic-gate 		return (NULL);
5997c478bd9Sstevel@tonic-gate 	}
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	sp = s;
6027c478bd9Sstevel@tonic-gate 	if (sp == NULL)
6037c478bd9Sstevel@tonic-gate 		sp = kmem_alloc(len + 1, KM_SLEEP);
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	/*
6067c478bd9Sstevel@tonic-gate 	 * At least check for embedded nulls
6077c478bd9Sstevel@tonic-gate 	 */
6087c478bd9Sstevel@tonic-gate 	for (i = 0; i < len; i++) {
6097c478bd9Sstevel@tonic-gate 		sp[i] = u8p[i];
6107c478bd9Sstevel@tonic-gate 		if (u8p[i] == '\0') {
6117c478bd9Sstevel@tonic-gate #ifdef	DEBUG
6127c478bd9Sstevel@tonic-gate 			zcmn_err(getzoneid(), CE_WARN,
6137c478bd9Sstevel@tonic-gate 			    "Embedded NULL in UTF-8 string");
6147c478bd9Sstevel@tonic-gate #endif
6157c478bd9Sstevel@tonic-gate 			if (s == NULL)
6167c478bd9Sstevel@tonic-gate 				kmem_free(sp, len + 1);
6177c478bd9Sstevel@tonic-gate 			return (NULL);
6187c478bd9Sstevel@tonic-gate 		}
6197c478bd9Sstevel@tonic-gate 	}
6207c478bd9Sstevel@tonic-gate 	sp[len] = '\0';
6217c478bd9Sstevel@tonic-gate 	*lenp = len + 1;
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	return (sp);
6247c478bd9Sstevel@tonic-gate }
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate /*
6277c478bd9Sstevel@tonic-gate  * str_to_utf8 - converts a null-terminated C string to a utf8 string
6287c478bd9Sstevel@tonic-gate  */
6297c478bd9Sstevel@tonic-gate utf8string *
6307c478bd9Sstevel@tonic-gate str_to_utf8(char *nm, utf8string *str)
6317c478bd9Sstevel@tonic-gate {
6327c478bd9Sstevel@tonic-gate 	int len;
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 	if (str == NULL)
6357c478bd9Sstevel@tonic-gate 		return (NULL);
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	if (nm == NULL || *nm == '\0') {
6387c478bd9Sstevel@tonic-gate 		str->utf8string_len = 0;
6397c478bd9Sstevel@tonic-gate 		str->utf8string_val = NULL;
6407c478bd9Sstevel@tonic-gate 	}
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate 	len = strlen(nm);
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	str->utf8string_val = kmem_alloc(len, KM_SLEEP);
6457c478bd9Sstevel@tonic-gate 	str->utf8string_len = len;
6467c478bd9Sstevel@tonic-gate 	bcopy(nm, str->utf8string_val, len);
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	return (str);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate utf8string *
6527c478bd9Sstevel@tonic-gate utf8_copy(utf8string *src, utf8string *dest)
6537c478bd9Sstevel@tonic-gate {
6547c478bd9Sstevel@tonic-gate 	if (src == NULL)
6557c478bd9Sstevel@tonic-gate 		return (NULL);
6567c478bd9Sstevel@tonic-gate 	if (dest == NULL)
6577c478bd9Sstevel@tonic-gate 		return (NULL);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	if (src->utf8string_len > 0) {
6607c478bd9Sstevel@tonic-gate 		dest->utf8string_val = kmem_alloc(src->utf8string_len,
6617c478bd9Sstevel@tonic-gate 			KM_SLEEP);
6627c478bd9Sstevel@tonic-gate 		bcopy(src->utf8string_val, dest->utf8string_val,
6637c478bd9Sstevel@tonic-gate 			src->utf8string_len);
6647c478bd9Sstevel@tonic-gate 		dest->utf8string_len = src->utf8string_len;
6657c478bd9Sstevel@tonic-gate 	} else {
6667c478bd9Sstevel@tonic-gate 		dest->utf8string_val = NULL;
6677c478bd9Sstevel@tonic-gate 		dest->utf8string_len = 0;
6687c478bd9Sstevel@tonic-gate 	}
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	return (dest);
6717c478bd9Sstevel@tonic-gate }
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate int
6747c478bd9Sstevel@tonic-gate utf8_compare(const utf8string *a, const utf8string *b)
6757c478bd9Sstevel@tonic-gate {
6767c478bd9Sstevel@tonic-gate 	int mlen, cmp;
6777c478bd9Sstevel@tonic-gate 	int alen, blen;
6787c478bd9Sstevel@tonic-gate 	char *aval, *bval;
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	if ((a == NULL) && (b == NULL))
6817c478bd9Sstevel@tonic-gate 		return (0);
6827c478bd9Sstevel@tonic-gate 	else if (a == NULL)
6837c478bd9Sstevel@tonic-gate 		return (-1);
6847c478bd9Sstevel@tonic-gate 	else if (b == NULL)
6857c478bd9Sstevel@tonic-gate 		return (1);
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 	alen = a->utf8string_len;
6887c478bd9Sstevel@tonic-gate 	blen = b->utf8string_len;
6897c478bd9Sstevel@tonic-gate 	aval = a->utf8string_val;
6907c478bd9Sstevel@tonic-gate 	bval = b->utf8string_val;
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 	if (((alen == 0) || (aval == NULL)) &&
6937c478bd9Sstevel@tonic-gate 	    ((blen == 0) || (bval == NULL)))
6947c478bd9Sstevel@tonic-gate 		return (0);
6957c478bd9Sstevel@tonic-gate 	else if ((alen == 0) || (aval == NULL))
6967c478bd9Sstevel@tonic-gate 		return (-1);
6977c478bd9Sstevel@tonic-gate 	else if ((blen == 0) || (bval == NULL))
6987c478bd9Sstevel@tonic-gate 		return (1);
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 	mlen = MIN(alen, blen);
7017c478bd9Sstevel@tonic-gate 	cmp = strncmp(aval, bval, mlen);
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 	if ((cmp == 0) && (alen == blen))
7047c478bd9Sstevel@tonic-gate 		return (0);
7057c478bd9Sstevel@tonic-gate 	else if ((cmp == 0) && (alen < blen))
7067c478bd9Sstevel@tonic-gate 		return (-1);
7077c478bd9Sstevel@tonic-gate 	else if (cmp == 0)
7087c478bd9Sstevel@tonic-gate 		return (1);
7097c478bd9Sstevel@tonic-gate 	else if (cmp < 0)
7107c478bd9Sstevel@tonic-gate 		return (-1);
7117c478bd9Sstevel@tonic-gate 	return (1);
7127c478bd9Sstevel@tonic-gate }
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate /*
7157c478bd9Sstevel@tonic-gate  * utf8_dir_verify - checks that the utf8 string is valid
7167c478bd9Sstevel@tonic-gate  */
7177c478bd9Sstevel@tonic-gate int
7187c478bd9Sstevel@tonic-gate utf8_dir_verify(utf8string *str)
7197c478bd9Sstevel@tonic-gate {
7207c478bd9Sstevel@tonic-gate 	char *nm;
7217c478bd9Sstevel@tonic-gate 	int len;
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 	if (str == NULL)
7247c478bd9Sstevel@tonic-gate 		return (0);
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	nm = str->utf8string_val;
7277c478bd9Sstevel@tonic-gate 	len = str->utf8string_len;
7287c478bd9Sstevel@tonic-gate 	if (nm == NULL || len == 0) {
7297c478bd9Sstevel@tonic-gate 		return (0);
7307c478bd9Sstevel@tonic-gate 	}
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 	if (len == 1 && nm[0] == '.')
7337c478bd9Sstevel@tonic-gate 		return (0);
7347c478bd9Sstevel@tonic-gate 	if (len == 2 && nm[0] == '.' && nm[1] == '.')
7357c478bd9Sstevel@tonic-gate 		return (0);
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate 	if (utf8_strchr(str, '/') != NULL)
7387c478bd9Sstevel@tonic-gate 		return (0);
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 	if (utf8_strchr(str, '\0') != NULL)
7417c478bd9Sstevel@tonic-gate 		return (0);
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate 	return (1);
7447c478bd9Sstevel@tonic-gate }
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate /*
7477c478bd9Sstevel@tonic-gate  * from rpcsec module (common/rpcsec)
7487c478bd9Sstevel@tonic-gate  */
7497c478bd9Sstevel@tonic-gate extern int sec_clnt_geth(CLIENT *, struct sec_data *, cred_t *, AUTH **);
7507c478bd9Sstevel@tonic-gate extern void sec_clnt_freeh(AUTH *);
7517c478bd9Sstevel@tonic-gate extern void sec_clnt_freeinfo(struct sec_data *);
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate /*
7547c478bd9Sstevel@tonic-gate  * authget() gets an auth handle based on the security
7557c478bd9Sstevel@tonic-gate  * information from the servinfo in mountinfo.
7567c478bd9Sstevel@tonic-gate  * The auth handle is stored in ch_client->cl_auth.
7577c478bd9Sstevel@tonic-gate  *
7587c478bd9Sstevel@tonic-gate  * First security flavor of choice is to use sv_secdata
7597c478bd9Sstevel@tonic-gate  * which is initiated by the client. If that fails, get
7607c478bd9Sstevel@tonic-gate  * secinfo from the server and then select one from the
7617c478bd9Sstevel@tonic-gate  * server secinfo list .
7627c478bd9Sstevel@tonic-gate  *
7637c478bd9Sstevel@tonic-gate  * For RPCSEC_GSS flavor, upon success, a secure context is
7647c478bd9Sstevel@tonic-gate  * established between client and server.
7657c478bd9Sstevel@tonic-gate  */
7667c478bd9Sstevel@tonic-gate int
7677c478bd9Sstevel@tonic-gate authget(servinfo4_t *svp, CLIENT *ch_client, cred_t *cr)
7687c478bd9Sstevel@tonic-gate {
7697c478bd9Sstevel@tonic-gate 	int error, i;
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	/*
7727c478bd9Sstevel@tonic-gate 	 * SV4_TRYSECINFO indicates to try the secinfo list from
7737c478bd9Sstevel@tonic-gate 	 * sv_secinfo until a successful one is reached. Point
7747c478bd9Sstevel@tonic-gate 	 * sv_currsec to the selected security mechanism for
7757c478bd9Sstevel@tonic-gate 	 * later sessions.
7767c478bd9Sstevel@tonic-gate 	 */
7777c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
7787c478bd9Sstevel@tonic-gate 	if ((svp->sv_flags & SV4_TRYSECINFO) && svp->sv_secinfo) {
7797c478bd9Sstevel@tonic-gate 		for (i = svp->sv_secinfo->index; i < svp->sv_secinfo->count;
7807c478bd9Sstevel@tonic-gate 								i++) {
7817c478bd9Sstevel@tonic-gate 			if (!(error = sec_clnt_geth(ch_client,
7827c478bd9Sstevel@tonic-gate 				&svp->sv_secinfo->sdata[i],
7837c478bd9Sstevel@tonic-gate 				cr, &ch_client->cl_auth))) {
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 				svp->sv_currsec = &svp->sv_secinfo->sdata[i];
7867c478bd9Sstevel@tonic-gate 				svp->sv_secinfo->index = i;
7877c478bd9Sstevel@tonic-gate 				/* done */
7887c478bd9Sstevel@tonic-gate 				svp->sv_flags &= ~SV4_TRYSECINFO;
7897c478bd9Sstevel@tonic-gate 				break;
7907c478bd9Sstevel@tonic-gate 			}
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 			/*
7937c478bd9Sstevel@tonic-gate 			 * Allow the caller retry with the security flavor
7947c478bd9Sstevel@tonic-gate 			 * pointed by svp->sv_secinfo->index when
7957c478bd9Sstevel@tonic-gate 			 * ETIMEDOUT/ECONNRESET occurs.
7967c478bd9Sstevel@tonic-gate 			 */
7977c478bd9Sstevel@tonic-gate 			if (error == ETIMEDOUT || error == ECONNRESET) {
7987c478bd9Sstevel@tonic-gate 				svp->sv_secinfo->index = i;
7997c478bd9Sstevel@tonic-gate 				break;
8007c478bd9Sstevel@tonic-gate 			}
8017c478bd9Sstevel@tonic-gate 		}
8027c478bd9Sstevel@tonic-gate 	} else {
8037c478bd9Sstevel@tonic-gate 		/* sv_currsec points to one of the entries in sv_secinfo */
8047c478bd9Sstevel@tonic-gate 		if (svp->sv_currsec) {
8057c478bd9Sstevel@tonic-gate 			error = sec_clnt_geth(ch_client, svp->sv_currsec, cr,
8067c478bd9Sstevel@tonic-gate 				&ch_client->cl_auth);
8077c478bd9Sstevel@tonic-gate 		} else {
8087c478bd9Sstevel@tonic-gate 			/* If it's null, use sv_secdata. */
8097c478bd9Sstevel@tonic-gate 			error = sec_clnt_geth(ch_client, svp->sv_secdata, cr,
8107c478bd9Sstevel@tonic-gate 				&ch_client->cl_auth);
8117c478bd9Sstevel@tonic-gate 		}
8127c478bd9Sstevel@tonic-gate 	}
8137c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 	return (error);
8167c478bd9Sstevel@tonic-gate }
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate /*
8197c478bd9Sstevel@tonic-gate  * Common handle get program for NFS, NFS ACL, and NFS AUTH client.
8207c478bd9Sstevel@tonic-gate  */
8217c478bd9Sstevel@tonic-gate int
8227c478bd9Sstevel@tonic-gate clget4(clinfo_t *ci, servinfo4_t *svp, cred_t *cr, CLIENT **newcl,
8237c478bd9Sstevel@tonic-gate     struct chtab **chp, struct nfs4_clnt *nfscl)
8247c478bd9Sstevel@tonic-gate {
8257c478bd9Sstevel@tonic-gate 	struct chhead *ch, *newch;
8267c478bd9Sstevel@tonic-gate 	struct chhead **plistp;
8277c478bd9Sstevel@tonic-gate 	struct chtab *cp;
8287c478bd9Sstevel@tonic-gate 	int error;
8297c478bd9Sstevel@tonic-gate 	k_sigset_t smask;
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 	if (newcl == NULL || chp == NULL || ci == NULL)
8327c478bd9Sstevel@tonic-gate 		return (EINVAL);
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 	*newcl = NULL;
8357c478bd9Sstevel@tonic-gate 	*chp = NULL;
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 	/*
8387c478bd9Sstevel@tonic-gate 	 * Find an unused handle or create one
8397c478bd9Sstevel@tonic-gate 	 */
8407c478bd9Sstevel@tonic-gate 	newch = NULL;
8417c478bd9Sstevel@tonic-gate 	nfscl->nfscl_stat.clgets.value.ui64++;
8427c478bd9Sstevel@tonic-gate top:
8437c478bd9Sstevel@tonic-gate 	/*
8447c478bd9Sstevel@tonic-gate 	 * Find the correct entry in the cache to check for free
8457c478bd9Sstevel@tonic-gate 	 * client handles.  The search is based on the RPC program
8467c478bd9Sstevel@tonic-gate 	 * number, program version number, dev_t for the transport
8477c478bd9Sstevel@tonic-gate 	 * device, and the protocol family.
8487c478bd9Sstevel@tonic-gate 	 */
8497c478bd9Sstevel@tonic-gate 	mutex_enter(&nfscl->nfscl_chtable4_lock);
8507c478bd9Sstevel@tonic-gate 	plistp = &nfscl->nfscl_chtable4;
8517c478bd9Sstevel@tonic-gate 	for (ch = nfscl->nfscl_chtable4; ch != NULL; ch = ch->ch_next) {
8527c478bd9Sstevel@tonic-gate 		if (ch->ch_prog == ci->cl_prog &&
8537c478bd9Sstevel@tonic-gate 		    ch->ch_vers == ci->cl_vers &&
8547c478bd9Sstevel@tonic-gate 		    ch->ch_dev == svp->sv_knconf->knc_rdev &&
8557c478bd9Sstevel@tonic-gate 		    (strcmp(ch->ch_protofmly,
8567c478bd9Sstevel@tonic-gate 			svp->sv_knconf->knc_protofmly) == 0))
8577c478bd9Sstevel@tonic-gate 			break;
8587c478bd9Sstevel@tonic-gate 		plistp = &ch->ch_next;
8597c478bd9Sstevel@tonic-gate 	}
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	/*
8627c478bd9Sstevel@tonic-gate 	 * If we didn't find a cache entry for this quadruple, then
8637c478bd9Sstevel@tonic-gate 	 * create one.  If we don't have one already preallocated,
8647c478bd9Sstevel@tonic-gate 	 * then drop the cache lock, create one, and then start over.
8657c478bd9Sstevel@tonic-gate 	 * If we did have a preallocated entry, then just add it to
8667c478bd9Sstevel@tonic-gate 	 * the front of the list.
8677c478bd9Sstevel@tonic-gate 	 */
8687c478bd9Sstevel@tonic-gate 	if (ch == NULL) {
8697c478bd9Sstevel@tonic-gate 		if (newch == NULL) {
8707c478bd9Sstevel@tonic-gate 			mutex_exit(&nfscl->nfscl_chtable4_lock);
8717c478bd9Sstevel@tonic-gate 			newch = kmem_alloc(sizeof (*newch), KM_SLEEP);
8727c478bd9Sstevel@tonic-gate 			newch->ch_timesused = 0;
8737c478bd9Sstevel@tonic-gate 			newch->ch_prog = ci->cl_prog;
8747c478bd9Sstevel@tonic-gate 			newch->ch_vers = ci->cl_vers;
8757c478bd9Sstevel@tonic-gate 			newch->ch_dev = svp->sv_knconf->knc_rdev;
8767c478bd9Sstevel@tonic-gate 			newch->ch_protofmly = kmem_alloc(
8777c478bd9Sstevel@tonic-gate 			    strlen(svp->sv_knconf->knc_protofmly) + 1,
8787c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
8797c478bd9Sstevel@tonic-gate 			(void) strcpy(newch->ch_protofmly,
8807c478bd9Sstevel@tonic-gate 			    svp->sv_knconf->knc_protofmly);
8817c478bd9Sstevel@tonic-gate 			newch->ch_list = NULL;
8827c478bd9Sstevel@tonic-gate 			goto top;
8837c478bd9Sstevel@tonic-gate 		}
8847c478bd9Sstevel@tonic-gate 		ch = newch;
8857c478bd9Sstevel@tonic-gate 		newch = NULL;
8867c478bd9Sstevel@tonic-gate 		ch->ch_next = nfscl->nfscl_chtable4;
8877c478bd9Sstevel@tonic-gate 		nfscl->nfscl_chtable4 = ch;
8887c478bd9Sstevel@tonic-gate 	/*
8897c478bd9Sstevel@tonic-gate 	 * We found a cache entry, but if it isn't on the front of the
8907c478bd9Sstevel@tonic-gate 	 * list, then move it to the front of the list to try to take
8917c478bd9Sstevel@tonic-gate 	 * advantage of locality of operations.
8927c478bd9Sstevel@tonic-gate 	 */
8937c478bd9Sstevel@tonic-gate 	} else if (ch != nfscl->nfscl_chtable4) {
8947c478bd9Sstevel@tonic-gate 		*plistp = ch->ch_next;
8957c478bd9Sstevel@tonic-gate 		ch->ch_next = nfscl->nfscl_chtable4;
8967c478bd9Sstevel@tonic-gate 		nfscl->nfscl_chtable4 = ch;
8977c478bd9Sstevel@tonic-gate 	}
8987c478bd9Sstevel@tonic-gate 
8997c478bd9Sstevel@tonic-gate 	/*
9007c478bd9Sstevel@tonic-gate 	 * If there was a free client handle cached, then remove it
9017c478bd9Sstevel@tonic-gate 	 * from the list, init it, and use it.
9027c478bd9Sstevel@tonic-gate 	 */
9037c478bd9Sstevel@tonic-gate 	if (ch->ch_list != NULL) {
9047c478bd9Sstevel@tonic-gate 		cp = ch->ch_list;
9057c478bd9Sstevel@tonic-gate 		ch->ch_list = cp->ch_list;
9067c478bd9Sstevel@tonic-gate 		mutex_exit(&nfscl->nfscl_chtable4_lock);
9077c478bd9Sstevel@tonic-gate 		if (newch != NULL) {
9087c478bd9Sstevel@tonic-gate 			kmem_free(newch->ch_protofmly,
9097c478bd9Sstevel@tonic-gate 			    strlen(newch->ch_protofmly) + 1);
9107c478bd9Sstevel@tonic-gate 			kmem_free(newch, sizeof (*newch));
9117c478bd9Sstevel@tonic-gate 		}
9127c478bd9Sstevel@tonic-gate 		(void) clnt_tli_kinit(cp->ch_client, svp->sv_knconf,
9137c478bd9Sstevel@tonic-gate 		    &svp->sv_addr, ci->cl_readsize, ci->cl_retrans, cr);
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 		/*
9167c478bd9Sstevel@tonic-gate 		 * Get an auth handle.
9177c478bd9Sstevel@tonic-gate 		 */
9187c478bd9Sstevel@tonic-gate 		error = authget(svp, cp->ch_client, cr);
9197c478bd9Sstevel@tonic-gate 		if (error || cp->ch_client->cl_auth == NULL) {
9207c478bd9Sstevel@tonic-gate 			CLNT_DESTROY(cp->ch_client);
9217c478bd9Sstevel@tonic-gate 			kmem_cache_free(chtab4_cache, cp);
9227c478bd9Sstevel@tonic-gate 			return ((error != 0) ? error : EINTR);
9237c478bd9Sstevel@tonic-gate 		}
9247c478bd9Sstevel@tonic-gate 		ch->ch_timesused++;
9257c478bd9Sstevel@tonic-gate 		*newcl = cp->ch_client;
9267c478bd9Sstevel@tonic-gate 		*chp = cp;
9277c478bd9Sstevel@tonic-gate 		return (0);
9287c478bd9Sstevel@tonic-gate 	}
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	/*
9317c478bd9Sstevel@tonic-gate 	 * There weren't any free client handles which fit, so allocate
9327c478bd9Sstevel@tonic-gate 	 * a new one and use that.
9337c478bd9Sstevel@tonic-gate 	 */
9347c478bd9Sstevel@tonic-gate #ifdef DEBUG
9357c478bd9Sstevel@tonic-gate 	atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, 1);
9367c478bd9Sstevel@tonic-gate #endif
9377c478bd9Sstevel@tonic-gate 	mutex_exit(&nfscl->nfscl_chtable4_lock);
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 	nfscl->nfscl_stat.cltoomany.value.ui64++;
9407c478bd9Sstevel@tonic-gate 	if (newch != NULL) {
9417c478bd9Sstevel@tonic-gate 		kmem_free(newch->ch_protofmly, strlen(newch->ch_protofmly) + 1);
9427c478bd9Sstevel@tonic-gate 		kmem_free(newch, sizeof (*newch));
9437c478bd9Sstevel@tonic-gate 	}
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 	cp = kmem_cache_alloc(chtab4_cache, KM_SLEEP);
9467c478bd9Sstevel@tonic-gate 	cp->ch_head = ch;
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	sigintr(&smask, (int)ci->cl_flags & MI4_INT);
9497c478bd9Sstevel@tonic-gate 	error = clnt_tli_kcreate(svp->sv_knconf, &svp->sv_addr, ci->cl_prog,
9507c478bd9Sstevel@tonic-gate 	    ci->cl_vers, ci->cl_readsize, ci->cl_retrans, cr, &cp->ch_client);
9517c478bd9Sstevel@tonic-gate 	sigunintr(&smask);
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 	if (error != 0) {
9547c478bd9Sstevel@tonic-gate 		kmem_cache_free(chtab4_cache, cp);
9557c478bd9Sstevel@tonic-gate #ifdef DEBUG
9567c478bd9Sstevel@tonic-gate 		atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, -1);
9577c478bd9Sstevel@tonic-gate #endif
9587c478bd9Sstevel@tonic-gate 		/*
9597c478bd9Sstevel@tonic-gate 		 * Warning is unnecessary if error is EINTR.
9607c478bd9Sstevel@tonic-gate 		 */
9617c478bd9Sstevel@tonic-gate 		if (error != EINTR) {
9627c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
9637c478bd9Sstevel@tonic-gate 			    "clget: couldn't create handle: %m\n");
9647c478bd9Sstevel@tonic-gate 		}
9657c478bd9Sstevel@tonic-gate 		return (error);
9667c478bd9Sstevel@tonic-gate 	}
9677c478bd9Sstevel@tonic-gate 	(void) CLNT_CONTROL(cp->ch_client, CLSET_PROGRESS, NULL);
9687c478bd9Sstevel@tonic-gate 	auth_destroy(cp->ch_client->cl_auth);
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 	/*
9717c478bd9Sstevel@tonic-gate 	 * Get an auth handle.
9727c478bd9Sstevel@tonic-gate 	 */
9737c478bd9Sstevel@tonic-gate 	error = authget(svp, cp->ch_client, cr);
9747c478bd9Sstevel@tonic-gate 	if (error || cp->ch_client->cl_auth == NULL) {
9757c478bd9Sstevel@tonic-gate 		CLNT_DESTROY(cp->ch_client);
9767c478bd9Sstevel@tonic-gate 		kmem_cache_free(chtab4_cache, cp);
9777c478bd9Sstevel@tonic-gate #ifdef DEBUG
9787c478bd9Sstevel@tonic-gate 		atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, -1);
9797c478bd9Sstevel@tonic-gate #endif
9807c478bd9Sstevel@tonic-gate 		return ((error != 0) ? error : EINTR);
9817c478bd9Sstevel@tonic-gate 	}
9827c478bd9Sstevel@tonic-gate 	ch->ch_timesused++;
9837c478bd9Sstevel@tonic-gate 	*newcl = cp->ch_client;
9847c478bd9Sstevel@tonic-gate 	ASSERT(cp->ch_client->cl_nosignal == FALSE);
9857c478bd9Sstevel@tonic-gate 	*chp = cp;
9867c478bd9Sstevel@tonic-gate 	return (0);
9877c478bd9Sstevel@tonic-gate }
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate static int
9907c478bd9Sstevel@tonic-gate nfs_clget4(mntinfo4_t *mi, servinfo4_t *svp, cred_t *cr, CLIENT **newcl,
9917c478bd9Sstevel@tonic-gate     struct chtab **chp, struct nfs4_clnt *nfscl)
9927c478bd9Sstevel@tonic-gate {
9937c478bd9Sstevel@tonic-gate 	clinfo_t ci;
9947c478bd9Sstevel@tonic-gate 	bool_t is_recov;
9957c478bd9Sstevel@tonic-gate 	int firstcall, error = 0;
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	/*
9987c478bd9Sstevel@tonic-gate 	 * Set read buffer size to rsize
9997c478bd9Sstevel@tonic-gate 	 * and add room for RPC headers.
10007c478bd9Sstevel@tonic-gate 	 */
10017c478bd9Sstevel@tonic-gate 	ci.cl_readsize = mi->mi_tsize;
10027c478bd9Sstevel@tonic-gate 	if (ci.cl_readsize != 0)
10037c478bd9Sstevel@tonic-gate 		ci.cl_readsize += (RPC_MAXDATASIZE - NFS_MAXDATA);
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate 	/*
10067c478bd9Sstevel@tonic-gate 	 * If soft mount and server is down just try once.
10077c478bd9Sstevel@tonic-gate 	 * meaning: do not retransmit.
10087c478bd9Sstevel@tonic-gate 	 */
10097c478bd9Sstevel@tonic-gate 	if (!(mi->mi_flags & MI4_HARD) && (mi->mi_flags & MI4_DOWN))
10107c478bd9Sstevel@tonic-gate 		ci.cl_retrans = 0;
10117c478bd9Sstevel@tonic-gate 	else
10127c478bd9Sstevel@tonic-gate 		ci.cl_retrans = mi->mi_retrans;
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	ci.cl_prog = mi->mi_prog;
10157c478bd9Sstevel@tonic-gate 	ci.cl_vers = mi->mi_vers;
10167c478bd9Sstevel@tonic-gate 	ci.cl_flags = mi->mi_flags;
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 	/*
10197c478bd9Sstevel@tonic-gate 	 * clget4 calls authget() to get an auth handle. For RPCSEC_GSS
10207c478bd9Sstevel@tonic-gate 	 * security flavor, the client tries to establish a security context
10217c478bd9Sstevel@tonic-gate 	 * by contacting the server. If the connection is timed out or reset,
10227c478bd9Sstevel@tonic-gate 	 * e.g. server reboot, we will try again.
10237c478bd9Sstevel@tonic-gate 	 */
10247c478bd9Sstevel@tonic-gate 	is_recov = (curthread == mi->mi_recovthread);
10257c478bd9Sstevel@tonic-gate 	firstcall = 1;
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	do {
10287c478bd9Sstevel@tonic-gate 		error = clget4(&ci, svp, cr, newcl, chp, nfscl);
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 		if (error == 0)
10317c478bd9Sstevel@tonic-gate 			break;
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 		/*
10347c478bd9Sstevel@tonic-gate 		 * For forced unmount and zone shutdown, bail out but
10357c478bd9Sstevel@tonic-gate 		 * let the recovery thread do one more transmission.
10367c478bd9Sstevel@tonic-gate 		 */
10377c478bd9Sstevel@tonic-gate 		if ((FS_OR_ZONE_GONE4(mi->mi_vfsp)) &&
10387c478bd9Sstevel@tonic-gate 		    (!is_recov || !firstcall)) {
10397c478bd9Sstevel@tonic-gate 			error = EIO;
10407c478bd9Sstevel@tonic-gate 			break;
10417c478bd9Sstevel@tonic-gate 		}
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 		/* do not retry for soft mount */
10447c478bd9Sstevel@tonic-gate 		if (!(mi->mi_flags & MI4_HARD))
10457c478bd9Sstevel@tonic-gate 			break;
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate 		/* let the caller deal with the failover case */
10487c478bd9Sstevel@tonic-gate 		if (FAILOVER_MOUNT4(mi))
10497c478bd9Sstevel@tonic-gate 			break;
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 		firstcall = 0;
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	} while (error == ETIMEDOUT || error == ECONNRESET);
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 	return (error);
10567c478bd9Sstevel@tonic-gate }
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate void
10597c478bd9Sstevel@tonic-gate clfree4(CLIENT *cl, struct chtab *cp, struct nfs4_clnt *nfscl)
10607c478bd9Sstevel@tonic-gate {
10617c478bd9Sstevel@tonic-gate 	if (cl->cl_auth != NULL) {
10627c478bd9Sstevel@tonic-gate 		sec_clnt_freeh(cl->cl_auth);
10637c478bd9Sstevel@tonic-gate 		cl->cl_auth = NULL;
10647c478bd9Sstevel@tonic-gate 	}
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 	/*
10677c478bd9Sstevel@tonic-gate 	 * Timestamp this cache entry so that we know when it was last
10687c478bd9Sstevel@tonic-gate 	 * used.
10697c478bd9Sstevel@tonic-gate 	 */
10707c478bd9Sstevel@tonic-gate 	cp->ch_freed = gethrestime_sec();
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	/*
10737c478bd9Sstevel@tonic-gate 	 * Add the free client handle to the front of the list.
10747c478bd9Sstevel@tonic-gate 	 * This way, the list will be sorted in youngest to oldest
10757c478bd9Sstevel@tonic-gate 	 * order.
10767c478bd9Sstevel@tonic-gate 	 */
10777c478bd9Sstevel@tonic-gate 	mutex_enter(&nfscl->nfscl_chtable4_lock);
10787c478bd9Sstevel@tonic-gate 	cp->ch_list = cp->ch_head->ch_list;
10797c478bd9Sstevel@tonic-gate 	cp->ch_head->ch_list = cp;
10807c478bd9Sstevel@tonic-gate 	mutex_exit(&nfscl->nfscl_chtable4_lock);
10817c478bd9Sstevel@tonic-gate }
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate #define	CL_HOLDTIME	60	/* time to hold client handles */
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate static void
10867c478bd9Sstevel@tonic-gate clreclaim4_zone(struct nfs4_clnt *nfscl, uint_t cl_holdtime)
10877c478bd9Sstevel@tonic-gate {
10887c478bd9Sstevel@tonic-gate 	struct chhead *ch;
10897c478bd9Sstevel@tonic-gate 	struct chtab *cp;	/* list of objects that can be reclaimed */
10907c478bd9Sstevel@tonic-gate 	struct chtab *cpe;
10917c478bd9Sstevel@tonic-gate 	struct chtab *cpl;
10927c478bd9Sstevel@tonic-gate 	struct chtab **cpp;
10937c478bd9Sstevel@tonic-gate #ifdef DEBUG
10947c478bd9Sstevel@tonic-gate 	int n = 0;
10957c478bd9Sstevel@tonic-gate 	clstat4_debug.clreclaim.value.ui64++;
10967c478bd9Sstevel@tonic-gate #endif
10977c478bd9Sstevel@tonic-gate 
10987c478bd9Sstevel@tonic-gate 	/*
10997c478bd9Sstevel@tonic-gate 	 * Need to reclaim some memory, so step through the cache
11007c478bd9Sstevel@tonic-gate 	 * looking through the lists for entries which can be freed.
11017c478bd9Sstevel@tonic-gate 	 */
11027c478bd9Sstevel@tonic-gate 	cp = NULL;
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 	mutex_enter(&nfscl->nfscl_chtable4_lock);
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	/*
11077c478bd9Sstevel@tonic-gate 	 * Here we step through each non-NULL quadruple and start to
11087c478bd9Sstevel@tonic-gate 	 * construct the reclaim list pointed to by cp.  Note that
11097c478bd9Sstevel@tonic-gate 	 * cp will contain all eligible chtab entries.  When this traversal
11107c478bd9Sstevel@tonic-gate 	 * completes, chtab entries from the last quadruple will be at the
11117c478bd9Sstevel@tonic-gate 	 * front of cp and entries from previously inspected quadruples have
11127c478bd9Sstevel@tonic-gate 	 * been appended to the rear of cp.
11137c478bd9Sstevel@tonic-gate 	 */
11147c478bd9Sstevel@tonic-gate 	for (ch = nfscl->nfscl_chtable4; ch != NULL; ch = ch->ch_next) {
11157c478bd9Sstevel@tonic-gate 		if (ch->ch_list == NULL)
11167c478bd9Sstevel@tonic-gate 			continue;
11177c478bd9Sstevel@tonic-gate 		/*
11187c478bd9Sstevel@tonic-gate 		 * Search each list for entries older then
11197c478bd9Sstevel@tonic-gate 		 * cl_holdtime seconds.  The lists are maintained
11207c478bd9Sstevel@tonic-gate 		 * in youngest to oldest order so that when the
11217c478bd9Sstevel@tonic-gate 		 * first entry is found which is old enough, then
11227c478bd9Sstevel@tonic-gate 		 * all of the rest of the entries on the list will
11237c478bd9Sstevel@tonic-gate 		 * be old enough as well.
11247c478bd9Sstevel@tonic-gate 		 */
11257c478bd9Sstevel@tonic-gate 		cpl = ch->ch_list;
11267c478bd9Sstevel@tonic-gate 		cpp = &ch->ch_list;
11277c478bd9Sstevel@tonic-gate 		while (cpl != NULL &&
11287c478bd9Sstevel@tonic-gate 			cpl->ch_freed + cl_holdtime > gethrestime_sec()) {
11297c478bd9Sstevel@tonic-gate 			cpp = &cpl->ch_list;
11307c478bd9Sstevel@tonic-gate 			cpl = cpl->ch_list;
11317c478bd9Sstevel@tonic-gate 		}
11327c478bd9Sstevel@tonic-gate 		if (cpl != NULL) {
11337c478bd9Sstevel@tonic-gate 			*cpp = NULL;
11347c478bd9Sstevel@tonic-gate 			if (cp != NULL) {
11357c478bd9Sstevel@tonic-gate 				cpe = cpl;
11367c478bd9Sstevel@tonic-gate 				while (cpe->ch_list != NULL)
11377c478bd9Sstevel@tonic-gate 					cpe = cpe->ch_list;
11387c478bd9Sstevel@tonic-gate 				cpe->ch_list = cp;
11397c478bd9Sstevel@tonic-gate 			}
11407c478bd9Sstevel@tonic-gate 			cp = cpl;
11417c478bd9Sstevel@tonic-gate 		}
11427c478bd9Sstevel@tonic-gate 	}
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	mutex_exit(&nfscl->nfscl_chtable4_lock);
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	/*
11477c478bd9Sstevel@tonic-gate 	 * If cp is empty, then there is nothing to reclaim here.
11487c478bd9Sstevel@tonic-gate 	 */
11497c478bd9Sstevel@tonic-gate 	if (cp == NULL)
11507c478bd9Sstevel@tonic-gate 		return;
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 	/*
11537c478bd9Sstevel@tonic-gate 	 * Step through the list of entries to free, destroying each client
11547c478bd9Sstevel@tonic-gate 	 * handle and kmem_free'ing the memory for each entry.
11557c478bd9Sstevel@tonic-gate 	 */
11567c478bd9Sstevel@tonic-gate 	while (cp != NULL) {
11577c478bd9Sstevel@tonic-gate #ifdef DEBUG
11587c478bd9Sstevel@tonic-gate 		n++;
11597c478bd9Sstevel@tonic-gate #endif
11607c478bd9Sstevel@tonic-gate 		CLNT_DESTROY(cp->ch_client);
11617c478bd9Sstevel@tonic-gate 		cpl = cp->ch_list;
11627c478bd9Sstevel@tonic-gate 		kmem_cache_free(chtab4_cache, cp);
11637c478bd9Sstevel@tonic-gate 		cp = cpl;
11647c478bd9Sstevel@tonic-gate 	}
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate #ifdef DEBUG
11677c478bd9Sstevel@tonic-gate 	/*
11687c478bd9Sstevel@tonic-gate 	 * Update clalloc so that nfsstat shows the current number
11697c478bd9Sstevel@tonic-gate 	 * of allocated client handles.
11707c478bd9Sstevel@tonic-gate 	 */
11717c478bd9Sstevel@tonic-gate 	atomic_add_64(&nfscl->nfscl_stat.clalloc.value.ui64, -n);
11727c478bd9Sstevel@tonic-gate #endif
11737c478bd9Sstevel@tonic-gate }
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate /* ARGSUSED */
11767c478bd9Sstevel@tonic-gate static void
11777c478bd9Sstevel@tonic-gate clreclaim4(void *all)
11787c478bd9Sstevel@tonic-gate {
11797c478bd9Sstevel@tonic-gate 	struct nfs4_clnt *nfscl;
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	/*
11827c478bd9Sstevel@tonic-gate 	 * The system is low on memory; go through and try to reclaim some from
11837c478bd9Sstevel@tonic-gate 	 * every zone on the system.
11847c478bd9Sstevel@tonic-gate 	 */
11857c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_clnt_list_lock);
11867c478bd9Sstevel@tonic-gate 	nfscl = list_head(&nfs4_clnt_list);
11877c478bd9Sstevel@tonic-gate 	for (; nfscl != NULL; nfscl = list_next(&nfs4_clnt_list, nfscl))
11887c478bd9Sstevel@tonic-gate 		clreclaim4_zone(nfscl, CL_HOLDTIME);
11897c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_clnt_list_lock);
11907c478bd9Sstevel@tonic-gate }
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate /*
11937c478bd9Sstevel@tonic-gate  * Minimum time-out values indexed by call type
11947c478bd9Sstevel@tonic-gate  * These units are in "eights" of a second to avoid multiplies
11957c478bd9Sstevel@tonic-gate  */
11967c478bd9Sstevel@tonic-gate static unsigned int minimum_timeo[] = {
11977c478bd9Sstevel@tonic-gate 	6, 7, 10
11987c478bd9Sstevel@tonic-gate };
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate #define	SHORTWAIT	(NFS_COTS_TIMEO / 10)
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate /*
12037c478bd9Sstevel@tonic-gate  * Back off for retransmission timeout, MAXTIMO is in hz of a sec
12047c478bd9Sstevel@tonic-gate  */
12057c478bd9Sstevel@tonic-gate #define	MAXTIMO	(20*hz)
12067c478bd9Sstevel@tonic-gate #define	backoff(tim)	(((tim) < MAXTIMO) ? dobackoff(tim) : (tim))
12077c478bd9Sstevel@tonic-gate #define	dobackoff(tim)	((((tim) << 1) > MAXTIMO) ? MAXTIMO : ((tim) << 1))
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate static int
12107c478bd9Sstevel@tonic-gate nfs4_rfscall(mntinfo4_t *mi, rpcproc_t which, xdrproc_t xdrargs, caddr_t argsp,
12117c478bd9Sstevel@tonic-gate     xdrproc_t xdrres, caddr_t resp, cred_t *cr, int *doqueue,
12127c478bd9Sstevel@tonic-gate     enum clnt_stat *rpc_statusp, int flags, struct nfs4_clnt *nfscl)
12137c478bd9Sstevel@tonic-gate {
12147c478bd9Sstevel@tonic-gate 	CLIENT *client;
12157c478bd9Sstevel@tonic-gate 	struct chtab *ch;
12167c478bd9Sstevel@tonic-gate 	struct rpc_err rpcerr;
12177c478bd9Sstevel@tonic-gate 	enum clnt_stat status;
12187c478bd9Sstevel@tonic-gate 	int error;
12197c478bd9Sstevel@tonic-gate 	struct timeval wait;
12207c478bd9Sstevel@tonic-gate 	int timeo;		/* in units of hz */
12217c478bd9Sstevel@tonic-gate 	bool_t tryagain, is_recov;
12227c478bd9Sstevel@tonic-gate 	k_sigset_t smask;
12237c478bd9Sstevel@tonic-gate 	servinfo4_t *svp;
12247c478bd9Sstevel@tonic-gate #ifdef DEBUG
12257c478bd9Sstevel@tonic-gate 	char *bufp;
12267c478bd9Sstevel@tonic-gate #endif
12277c478bd9Sstevel@tonic-gate 	int firstcall;
12287c478bd9Sstevel@tonic-gate 
12297c478bd9Sstevel@tonic-gate 	rpcerr.re_status = RPC_SUCCESS;
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 	/*
12327c478bd9Sstevel@tonic-gate 	 * If we know that we are rebooting then let's
12337c478bd9Sstevel@tonic-gate 	 * not bother with doing any over the wireness.
12347c478bd9Sstevel@tonic-gate 	 */
12357c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
12367c478bd9Sstevel@tonic-gate 	if (mi->mi_flags & MI4_SHUTDOWN) {
12377c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
12387c478bd9Sstevel@tonic-gate 		return (EIO);
12397c478bd9Sstevel@tonic-gate 	}
12407c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
12417c478bd9Sstevel@tonic-gate 
12427c478bd9Sstevel@tonic-gate 	/*
12437c478bd9Sstevel@tonic-gate 	 * clget() calls clnt_tli_kinit() which clears the xid, so we
12447c478bd9Sstevel@tonic-gate 	 * are guaranteed to reprocess the retry as a new request.
12457c478bd9Sstevel@tonic-gate 	 */
12467c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
12477c478bd9Sstevel@tonic-gate 	rpcerr.re_errno = nfs_clget4(mi, svp, cr, &client, &ch, nfscl);
12487c478bd9Sstevel@tonic-gate 	if (rpcerr.re_errno != 0)
12497c478bd9Sstevel@tonic-gate 		return (rpcerr.re_errno);
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 	timeo = (mi->mi_timeo * hz) / 10;
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	/*
12547c478bd9Sstevel@tonic-gate 	 * If hard mounted fs, retry call forever unless hard error
12557c478bd9Sstevel@tonic-gate 	 * occurs.
12567c478bd9Sstevel@tonic-gate 	 *
12577c478bd9Sstevel@tonic-gate 	 * For forced unmount, let the recovery thread through but return
12587c478bd9Sstevel@tonic-gate 	 * an error for all others.  This is so that user processes can
12597c478bd9Sstevel@tonic-gate 	 * exit quickly.  The recovery thread bails out after one
12607c478bd9Sstevel@tonic-gate 	 * transmission so that it can tell if it needs to continue.
12617c478bd9Sstevel@tonic-gate 	 *
12627c478bd9Sstevel@tonic-gate 	 * For zone shutdown, behave as above to encourage quick
12637c478bd9Sstevel@tonic-gate 	 * process exit, but also fail quickly when servers have
12647c478bd9Sstevel@tonic-gate 	 * timed out before and reduce the timeouts.
12657c478bd9Sstevel@tonic-gate 	 */
12667c478bd9Sstevel@tonic-gate 	is_recov = (curthread == mi->mi_recovthread);
12677c478bd9Sstevel@tonic-gate 	firstcall = 1;
12687c478bd9Sstevel@tonic-gate 	do {
12697c478bd9Sstevel@tonic-gate 		tryagain = FALSE;
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_rfscall_debug, (CE_NOTE,
12727c478bd9Sstevel@tonic-gate 			"nfs4_rfscall: vfs_flag=0x%x, %s",
12737c478bd9Sstevel@tonic-gate 			mi->mi_vfsp->vfs_flag,
12747c478bd9Sstevel@tonic-gate 			is_recov ? "recov thread" : "not recov thread"));
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 		/*
12777c478bd9Sstevel@tonic-gate 		 * It's possible while we're retrying the admin
12787c478bd9Sstevel@tonic-gate 		 * decided to reboot.
12797c478bd9Sstevel@tonic-gate 		 */
12807c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
12817c478bd9Sstevel@tonic-gate 		if (mi->mi_flags & MI4_SHUTDOWN) {
12827c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
12837c478bd9Sstevel@tonic-gate 			clfree4(client, ch, nfscl);
12847c478bd9Sstevel@tonic-gate 			return (EIO);
12857c478bd9Sstevel@tonic-gate 		}
12867c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
12877c478bd9Sstevel@tonic-gate 
12887c478bd9Sstevel@tonic-gate 		if ((mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED) &&
12897c478bd9Sstevel@tonic-gate 		    (!is_recov || !firstcall)) {
12907c478bd9Sstevel@tonic-gate 			clfree4(client, ch, nfscl);
12917c478bd9Sstevel@tonic-gate 			return (EIO);
12927c478bd9Sstevel@tonic-gate 		}
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 		if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) {
12957c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
12967c478bd9Sstevel@tonic-gate 			if ((mi->mi_flags & MI4_TIMEDOUT) ||
12977c478bd9Sstevel@tonic-gate 			    !is_recov || !firstcall) {
12987c478bd9Sstevel@tonic-gate 				mutex_exit(&mi->mi_lock);
12997c478bd9Sstevel@tonic-gate 				clfree4(client, ch, nfscl);
13007c478bd9Sstevel@tonic-gate 				return (EIO);
13017c478bd9Sstevel@tonic-gate 			}
13027c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
13037c478bd9Sstevel@tonic-gate 			timeo = (MIN(mi->mi_timeo, SHORTWAIT) * hz) / 10;
13047c478bd9Sstevel@tonic-gate 		}
13057c478bd9Sstevel@tonic-gate 
13067c478bd9Sstevel@tonic-gate 		firstcall = 0;
13077c478bd9Sstevel@tonic-gate 		TICK_TO_TIMEVAL(timeo, &wait);
13087c478bd9Sstevel@tonic-gate 
13097c478bd9Sstevel@tonic-gate 		/*
13107c478bd9Sstevel@tonic-gate 		 * Mask out all signals except SIGHUP, SIGINT, SIGQUIT
13117c478bd9Sstevel@tonic-gate 		 * and SIGTERM. (Preserving the existing masks).
13127c478bd9Sstevel@tonic-gate 		 * Mask out SIGINT if mount option nointr is specified.
13137c478bd9Sstevel@tonic-gate 		 */
13147c478bd9Sstevel@tonic-gate 		sigintr(&smask, (int)mi->mi_flags & MI4_INT);
13157c478bd9Sstevel@tonic-gate 		if (!(mi->mi_flags & MI4_INT))
13167c478bd9Sstevel@tonic-gate 			client->cl_nosignal = TRUE;
13177c478bd9Sstevel@tonic-gate 
13187c478bd9Sstevel@tonic-gate 		/*
13197c478bd9Sstevel@tonic-gate 		 * If there is a current signal, then don't bother
13207c478bd9Sstevel@tonic-gate 		 * even trying to send out the request because we
13217c478bd9Sstevel@tonic-gate 		 * won't be able to block waiting for the response.
13227c478bd9Sstevel@tonic-gate 		 * Simply assume RPC_INTR and get on with it.
13237c478bd9Sstevel@tonic-gate 		 */
13247c478bd9Sstevel@tonic-gate 		if (ttolwp(curthread) != NULL && ISSIG(curthread, JUSTLOOKING))
13257c478bd9Sstevel@tonic-gate 			status = RPC_INTR;
13267c478bd9Sstevel@tonic-gate 		else {
13277c478bd9Sstevel@tonic-gate 			status = CLNT_CALL(client, which, xdrargs, argsp,
13287c478bd9Sstevel@tonic-gate 			    xdrres, resp, wait);
13297c478bd9Sstevel@tonic-gate 		}
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 		if (!(mi->mi_flags & MI4_INT))
13327c478bd9Sstevel@tonic-gate 			client->cl_nosignal = FALSE;
13337c478bd9Sstevel@tonic-gate 		/*
13347c478bd9Sstevel@tonic-gate 		 * restore original signal mask
13357c478bd9Sstevel@tonic-gate 		 */
13367c478bd9Sstevel@tonic-gate 		sigunintr(&smask);
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate 		switch (status) {
13397c478bd9Sstevel@tonic-gate 		case RPC_SUCCESS:
13407c478bd9Sstevel@tonic-gate 			break;
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 		case RPC_INTR:
13437c478bd9Sstevel@tonic-gate 			/*
13447c478bd9Sstevel@tonic-gate 			 * There is no way to recover from this error,
13457c478bd9Sstevel@tonic-gate 			 * even if mount option nointr is specified.
13467c478bd9Sstevel@tonic-gate 			 * SIGKILL, for example, cannot be blocked.
13477c478bd9Sstevel@tonic-gate 			 */
13487c478bd9Sstevel@tonic-gate 			rpcerr.re_status = RPC_INTR;
13497c478bd9Sstevel@tonic-gate 			rpcerr.re_errno = EINTR;
13507c478bd9Sstevel@tonic-gate 			break;
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate 		case RPC_UDERROR:
13537c478bd9Sstevel@tonic-gate 			/*
13547c478bd9Sstevel@tonic-gate 			 * If the NFS server is local (vold) and
13557c478bd9Sstevel@tonic-gate 			 * it goes away then we get RPC_UDERROR.
13567c478bd9Sstevel@tonic-gate 			 * This is a retryable error, so we would
13577c478bd9Sstevel@tonic-gate 			 * loop, so check to see if the specific
13587c478bd9Sstevel@tonic-gate 			 * error was ECONNRESET, indicating that
13597c478bd9Sstevel@tonic-gate 			 * target did not exist at all.  If so,
13607c478bd9Sstevel@tonic-gate 			 * return with RPC_PROGUNAVAIL and
13617c478bd9Sstevel@tonic-gate 			 * ECONNRESET to indicate why.
13627c478bd9Sstevel@tonic-gate 			 */
13637c478bd9Sstevel@tonic-gate 			CLNT_GETERR(client, &rpcerr);
13647c478bd9Sstevel@tonic-gate 			if (rpcerr.re_errno == ECONNRESET) {
13657c478bd9Sstevel@tonic-gate 				rpcerr.re_status = RPC_PROGUNAVAIL;
13667c478bd9Sstevel@tonic-gate 				rpcerr.re_errno = ECONNRESET;
13677c478bd9Sstevel@tonic-gate 				break;
13687c478bd9Sstevel@tonic-gate 			}
13697c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate 		default:		/* probably RPC_TIMEDOUT */
13727c478bd9Sstevel@tonic-gate 
13737c478bd9Sstevel@tonic-gate 			if (IS_UNRECOVERABLE_RPC(status))
13747c478bd9Sstevel@tonic-gate 				break;
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate 			/*
13777c478bd9Sstevel@tonic-gate 			 * increment server not responding count
13787c478bd9Sstevel@tonic-gate 			 */
13797c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
13807c478bd9Sstevel@tonic-gate 			mi->mi_noresponse++;
13817c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
13827c478bd9Sstevel@tonic-gate #ifdef DEBUG
13837c478bd9Sstevel@tonic-gate 			nfscl->nfscl_stat.noresponse.value.ui64++;
13847c478bd9Sstevel@tonic-gate #endif
13857c478bd9Sstevel@tonic-gate 			/*
13867c478bd9Sstevel@tonic-gate 			 * On zone shutdown, mark server dead and move on.
13877c478bd9Sstevel@tonic-gate 			 */
13887c478bd9Sstevel@tonic-gate 			if (zone_status_get(curproc->p_zone) >=
13897c478bd9Sstevel@tonic-gate 			    ZONE_IS_SHUTTING_DOWN) {
13907c478bd9Sstevel@tonic-gate 				mutex_enter(&mi->mi_lock);
13917c478bd9Sstevel@tonic-gate 				mi->mi_flags |= MI4_TIMEDOUT;
13927c478bd9Sstevel@tonic-gate 				mutex_exit(&mi->mi_lock);
13937c478bd9Sstevel@tonic-gate 				clfree4(client, ch, nfscl);
13947c478bd9Sstevel@tonic-gate 				return (EIO);
13957c478bd9Sstevel@tonic-gate 			}
13967c478bd9Sstevel@tonic-gate 
13977c478bd9Sstevel@tonic-gate 			/*
13987c478bd9Sstevel@tonic-gate 			 * NFS client failover support:
13997c478bd9Sstevel@tonic-gate 			 * return and let the caller take care of
14007c478bd9Sstevel@tonic-gate 			 * failover.  We only return for failover mounts
14017c478bd9Sstevel@tonic-gate 			 * because otherwise we want the "not responding"
14027c478bd9Sstevel@tonic-gate 			 * message, the timer updates, etc.
14037c478bd9Sstevel@tonic-gate 			 */
14047c478bd9Sstevel@tonic-gate 			if (mi->mi_vers == 4 && FAILOVER_MOUNT4(mi) &&
14057c478bd9Sstevel@tonic-gate 			    (error = try_failover(status)) != 0) {
14067c478bd9Sstevel@tonic-gate 				clfree4(client, ch, nfscl);
14077c478bd9Sstevel@tonic-gate 				*rpc_statusp = status;
14087c478bd9Sstevel@tonic-gate 				return (error);
14097c478bd9Sstevel@tonic-gate 			}
14107c478bd9Sstevel@tonic-gate 
14117c478bd9Sstevel@tonic-gate 			if (flags & RFSCALL_SOFT)
14127c478bd9Sstevel@tonic-gate 				break;
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 			tryagain = TRUE;
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 			/*
14177c478bd9Sstevel@tonic-gate 			 * The call is in progress (over COTS).
14187c478bd9Sstevel@tonic-gate 			 * Try the CLNT_CALL again, but don't
14197c478bd9Sstevel@tonic-gate 			 * print a noisy error message.
14207c478bd9Sstevel@tonic-gate 			 */
14217c478bd9Sstevel@tonic-gate 			if (status == RPC_INPROGRESS)
14227c478bd9Sstevel@tonic-gate 				break;
14237c478bd9Sstevel@tonic-gate 
14247c478bd9Sstevel@tonic-gate 			timeo = backoff(timeo);
14257c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
14267c478bd9Sstevel@tonic-gate 			if (!(mi->mi_flags & MI4_PRINTED)) {
14277c478bd9Sstevel@tonic-gate 				mi->mi_flags |= MI4_PRINTED;
14287c478bd9Sstevel@tonic-gate 				mutex_exit(&mi->mi_lock);
14297c478bd9Sstevel@tonic-gate 				nfs4_queue_fact(RF_SRV_NOT_RESPOND, mi, 0, 0, 0,
14307c478bd9Sstevel@tonic-gate 				    FALSE, NULL, 0, NULL);
14317c478bd9Sstevel@tonic-gate 			} else
14327c478bd9Sstevel@tonic-gate 				mutex_exit(&mi->mi_lock);
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 			if (*doqueue && curproc->p_sessp->s_vp != NULL) {
14357c478bd9Sstevel@tonic-gate 				*doqueue = 0;
14367c478bd9Sstevel@tonic-gate 				if (!(mi->mi_flags & MI4_NOPRINT))
14377c478bd9Sstevel@tonic-gate 					nfs4_queue_fact(RF_SRV_NOT_RESPOND, mi,
14387c478bd9Sstevel@tonic-gate 					    0, 0, 0, FALSE, NULL, 0, NULL);
14397c478bd9Sstevel@tonic-gate 			}
14407c478bd9Sstevel@tonic-gate 		}
14417c478bd9Sstevel@tonic-gate 	} while (tryagain);
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate 	DTRACE_PROBE2(nfs4__rfscall_debug, enum clnt_stat, status,
14447c478bd9Sstevel@tonic-gate 			int, rpcerr.re_errno);
14457c478bd9Sstevel@tonic-gate 
14467c478bd9Sstevel@tonic-gate 	if (status != RPC_SUCCESS) {
14477c478bd9Sstevel@tonic-gate 		zoneid_t zoneid = mi->mi_zone->zone_id;
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 		/*
14507c478bd9Sstevel@tonic-gate 		 * Let soft mounts use the timed out message.
14517c478bd9Sstevel@tonic-gate 		 */
14527c478bd9Sstevel@tonic-gate 		if (status == RPC_INPROGRESS)
14537c478bd9Sstevel@tonic-gate 			status = RPC_TIMEDOUT;
14547c478bd9Sstevel@tonic-gate 		nfscl->nfscl_stat.badcalls.value.ui64++;
14557c478bd9Sstevel@tonic-gate 		if (status != RPC_INTR) {
14567c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
14577c478bd9Sstevel@tonic-gate 			mi->mi_flags |= MI4_DOWN;
14587c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
14597c478bd9Sstevel@tonic-gate 			CLNT_GETERR(client, &rpcerr);
14607c478bd9Sstevel@tonic-gate #ifdef DEBUG
14617c478bd9Sstevel@tonic-gate 			bufp = clnt_sperror(client, svp->sv_hostname);
14627c478bd9Sstevel@tonic-gate 			zprintf(zoneid, "NFS%d %s failed for %s\n",
14637c478bd9Sstevel@tonic-gate 			    mi->mi_vers, mi->mi_rfsnames[which], bufp);
14647c478bd9Sstevel@tonic-gate 			if (curproc->p_sessp->s_vp != NULL) {
14657c478bd9Sstevel@tonic-gate 				if (!(mi->mi_flags & MI4_NOPRINT)) {
14667c478bd9Sstevel@tonic-gate 					uprintf("NFS%d %s failed for %s\n",
14677c478bd9Sstevel@tonic-gate 					    mi->mi_vers, mi->mi_rfsnames[which],
14687c478bd9Sstevel@tonic-gate 					    bufp);
14697c478bd9Sstevel@tonic-gate 				}
14707c478bd9Sstevel@tonic-gate 			}
14717c478bd9Sstevel@tonic-gate 			kmem_free(bufp, MAXPATHLEN);
14727c478bd9Sstevel@tonic-gate #else
14737c478bd9Sstevel@tonic-gate 			zprintf(zoneid,
14747c478bd9Sstevel@tonic-gate 			    "NFS %s failed for server %s: error %d (%s)\n",
14757c478bd9Sstevel@tonic-gate 			    mi->mi_rfsnames[which], svp->sv_hostname,
14767c478bd9Sstevel@tonic-gate 			    status, clnt_sperrno(status));
14777c478bd9Sstevel@tonic-gate 			if (curproc->p_sessp->s_vp != NULL) {
14787c478bd9Sstevel@tonic-gate 				if (!(mi->mi_flags & MI4_NOPRINT)) {
14797c478bd9Sstevel@tonic-gate 					uprintf(
14807c478bd9Sstevel@tonic-gate 				"NFS %s failed for server %s: error %d (%s)\n",
14817c478bd9Sstevel@tonic-gate 					    mi->mi_rfsnames[which],
14827c478bd9Sstevel@tonic-gate 					    svp->sv_hostname, status,
14837c478bd9Sstevel@tonic-gate 					    clnt_sperrno(status));
14847c478bd9Sstevel@tonic-gate 				}
14857c478bd9Sstevel@tonic-gate 			}
14867c478bd9Sstevel@tonic-gate #endif
14877c478bd9Sstevel@tonic-gate 			/*
14887c478bd9Sstevel@tonic-gate 			 * when CLNT_CALL() fails with RPC_AUTHERROR,
14897c478bd9Sstevel@tonic-gate 			 * re_errno is set appropriately depending on
14907c478bd9Sstevel@tonic-gate 			 * the authentication error
14917c478bd9Sstevel@tonic-gate 			 */
14927c478bd9Sstevel@tonic-gate 			if (status == RPC_VERSMISMATCH ||
14937c478bd9Sstevel@tonic-gate 			    status == RPC_PROGVERSMISMATCH)
14947c478bd9Sstevel@tonic-gate 				rpcerr.re_errno = EIO;
14957c478bd9Sstevel@tonic-gate 		}
14967c478bd9Sstevel@tonic-gate 	} else {
14977c478bd9Sstevel@tonic-gate 		/*
14987c478bd9Sstevel@tonic-gate 		 * Test the value of mi_down and mi_printed without
14997c478bd9Sstevel@tonic-gate 		 * holding the mi_lock mutex.  If they are both zero,
15007c478bd9Sstevel@tonic-gate 		 * then it is okay to skip the down and printed
15017c478bd9Sstevel@tonic-gate 		 * processing.  This saves on a mutex_enter and
15027c478bd9Sstevel@tonic-gate 		 * mutex_exit pair for a normal, successful RPC.
15037c478bd9Sstevel@tonic-gate 		 * This was just complete overhead.
15047c478bd9Sstevel@tonic-gate 		 */
15057c478bd9Sstevel@tonic-gate 		if (mi->mi_flags & (MI4_DOWN | MI4_PRINTED)) {
15067c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
15077c478bd9Sstevel@tonic-gate 			mi->mi_flags &= ~MI4_DOWN;
15087c478bd9Sstevel@tonic-gate 			if (mi->mi_flags & MI4_PRINTED) {
15097c478bd9Sstevel@tonic-gate 				mi->mi_flags &= ~MI4_PRINTED;
15107c478bd9Sstevel@tonic-gate 				mutex_exit(&mi->mi_lock);
15117c478bd9Sstevel@tonic-gate 				if (!(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED))
15127c478bd9Sstevel@tonic-gate 					nfs4_queue_fact(RF_SRV_OK, mi, 0, 0,
15137c478bd9Sstevel@tonic-gate 					    0, FALSE, NULL, 0, NULL);
15147c478bd9Sstevel@tonic-gate 			} else
15157c478bd9Sstevel@tonic-gate 				mutex_exit(&mi->mi_lock);
15167c478bd9Sstevel@tonic-gate 		}
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate 		if (*doqueue == 0) {
15197c478bd9Sstevel@tonic-gate 			if (!(mi->mi_flags & MI4_NOPRINT) &&
15207c478bd9Sstevel@tonic-gate 			    !(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED))
15217c478bd9Sstevel@tonic-gate 				nfs4_queue_fact(RF_SRV_OK, mi, 0, 0, 0,
15227c478bd9Sstevel@tonic-gate 				    FALSE, NULL, 0, NULL);
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 			*doqueue = 1;
15257c478bd9Sstevel@tonic-gate 		}
15267c478bd9Sstevel@tonic-gate 	}
15277c478bd9Sstevel@tonic-gate 
15287c478bd9Sstevel@tonic-gate 	clfree4(client, ch, nfscl);
15297c478bd9Sstevel@tonic-gate 
15307c478bd9Sstevel@tonic-gate 	ASSERT(rpcerr.re_status == RPC_SUCCESS || rpcerr.re_errno != 0);
15317c478bd9Sstevel@tonic-gate 
15327c478bd9Sstevel@tonic-gate 	TRACE_1(TR_FAC_NFS, TR_RFSCALL_END, "nfs4_rfscall_end:errno %d",
15337c478bd9Sstevel@tonic-gate 	    rpcerr.re_errno);
15347c478bd9Sstevel@tonic-gate 
15357c478bd9Sstevel@tonic-gate 	*rpc_statusp = status;
15367c478bd9Sstevel@tonic-gate 	return (rpcerr.re_errno);
15377c478bd9Sstevel@tonic-gate }
15387c478bd9Sstevel@tonic-gate 
15397c478bd9Sstevel@tonic-gate /*
15407c478bd9Sstevel@tonic-gate  * rfs4call - general wrapper for RPC calls initiated by the client
15417c478bd9Sstevel@tonic-gate  */
15427c478bd9Sstevel@tonic-gate void
15437c478bd9Sstevel@tonic-gate rfs4call(mntinfo4_t *mi, COMPOUND4args_clnt *argsp, COMPOUND4res_clnt *resp,
15447c478bd9Sstevel@tonic-gate 	cred_t *cr, int *doqueue, int flags, nfs4_error_t *ep)
15457c478bd9Sstevel@tonic-gate {
15467c478bd9Sstevel@tonic-gate 	int i, error;
15477c478bd9Sstevel@tonic-gate 	enum clnt_stat rpc_status = NFS4_OK;
15487c478bd9Sstevel@tonic-gate 	int num_resops;
15497c478bd9Sstevel@tonic-gate 	struct nfs4_clnt *nfscl;
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 	ASSERT(curproc->p_zone == mi->mi_zone);
15527c478bd9Sstevel@tonic-gate 	nfscl = zone_getspecific(nfs4clnt_zone_key, curproc->p_zone);
15537c478bd9Sstevel@tonic-gate 	ASSERT(nfscl != NULL);
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate 	nfscl->nfscl_stat.calls.value.ui64++;
15567c478bd9Sstevel@tonic-gate 	mi->mi_reqs[NFSPROC4_COMPOUND].value.ui64++;
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	/* Set up the results struct for XDR usage */
15597c478bd9Sstevel@tonic-gate 	resp->argsp = argsp;
15607c478bd9Sstevel@tonic-gate 	resp->array = NULL;
15617c478bd9Sstevel@tonic-gate 	resp->status = 0;
15627c478bd9Sstevel@tonic-gate 	resp->decode_len = 0;
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	error = nfs4_rfscall(mi, NFSPROC4_COMPOUND,
15657c478bd9Sstevel@tonic-gate 		xdr_COMPOUND4args_clnt, (caddr_t)argsp,
15667c478bd9Sstevel@tonic-gate 		xdr_COMPOUND4res_clnt, (caddr_t)resp, cr,
15677c478bd9Sstevel@tonic-gate 		doqueue, &rpc_status, flags, nfscl);
15687c478bd9Sstevel@tonic-gate 
15697c478bd9Sstevel@tonic-gate 	/* Return now if it was an RPC error */
15707c478bd9Sstevel@tonic-gate 	if (error) {
15717c478bd9Sstevel@tonic-gate 		ep->error = error;
15727c478bd9Sstevel@tonic-gate 		ep->stat = resp->status;
15737c478bd9Sstevel@tonic-gate 		ep->rpc_status = rpc_status;
15747c478bd9Sstevel@tonic-gate 		return;
15757c478bd9Sstevel@tonic-gate 	}
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 	/* else we'll count the processed operations */
15787c478bd9Sstevel@tonic-gate 	num_resops = resp->decode_len;
15797c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_resops; i++) {
15807c478bd9Sstevel@tonic-gate 		/*
15817c478bd9Sstevel@tonic-gate 		 * Count the individual operations
15827c478bd9Sstevel@tonic-gate 		 * processed by the server.
15837c478bd9Sstevel@tonic-gate 		 */
15847c478bd9Sstevel@tonic-gate 		if (resp->array[i].resop >= NFSPROC4_NULL &&
15857c478bd9Sstevel@tonic-gate 		    resp->array[i].resop <= OP_WRITE)
15867c478bd9Sstevel@tonic-gate 			mi->mi_reqs[resp->array[i].resop].value.ui64++;
15877c478bd9Sstevel@tonic-gate 	}
15887c478bd9Sstevel@tonic-gate 
15897c478bd9Sstevel@tonic-gate 	ep->error = 0;
15907c478bd9Sstevel@tonic-gate 	ep->stat = resp->status;
15917c478bd9Sstevel@tonic-gate 	ep->rpc_status = rpc_status;
15927c478bd9Sstevel@tonic-gate }
15937c478bd9Sstevel@tonic-gate 
15947c478bd9Sstevel@tonic-gate /*
15957c478bd9Sstevel@tonic-gate  * nfs4rename_update - updates stored state after a rename.  Currently this
15967c478bd9Sstevel@tonic-gate  * is the path of the object and anything under it, and the filehandle of
15977c478bd9Sstevel@tonic-gate  * the renamed object.
15987c478bd9Sstevel@tonic-gate  */
15997c478bd9Sstevel@tonic-gate void
16007c478bd9Sstevel@tonic-gate nfs4rename_update(vnode_t *renvp, vnode_t *ndvp, nfs_fh4 *nfh4p, char *nnm)
16017c478bd9Sstevel@tonic-gate {
16027c478bd9Sstevel@tonic-gate 	sfh4_update(VTOR4(renvp)->r_fh, nfh4p);
16037c478bd9Sstevel@tonic-gate 	fn_move(VTOSV(renvp)->sv_name, VTOSV(ndvp)->sv_name, nnm);
16047c478bd9Sstevel@tonic-gate }
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate /*
16077c478bd9Sstevel@tonic-gate  * Routine to look up the filehandle for the given path and rootvp.
16087c478bd9Sstevel@tonic-gate  *
16097c478bd9Sstevel@tonic-gate  * Return values:
16107c478bd9Sstevel@tonic-gate  * - success: returns zero and *statp is set to NFS4_OK, and *fhp is
16117c478bd9Sstevel@tonic-gate  *   updated.
16127c478bd9Sstevel@tonic-gate  * - error: return value (errno value) and/or *statp is set appropriately.
16137c478bd9Sstevel@tonic-gate  */
16147c478bd9Sstevel@tonic-gate #define	RML_ORDINARY	1
16157c478bd9Sstevel@tonic-gate #define	RML_NAMED_ATTR	2
16167c478bd9Sstevel@tonic-gate #define	RML_ATTRDIR	3
16177c478bd9Sstevel@tonic-gate 
16187c478bd9Sstevel@tonic-gate static void
16197c478bd9Sstevel@tonic-gate remap_lookup(nfs4_fname_t *fname, vnode_t *rootvp,
16207c478bd9Sstevel@tonic-gate 	int filetype, cred_t *cr,
16217c478bd9Sstevel@tonic-gate 	nfs_fh4 *fhp, nfs4_ga_res_t *garp,	/* fh, attrs for object */
16227c478bd9Sstevel@tonic-gate 	nfs_fh4 *pfhp, nfs4_ga_res_t *pgarp,	/* fh, attrs for parent */
16237c478bd9Sstevel@tonic-gate 	nfs4_error_t *ep)
16247c478bd9Sstevel@tonic-gate {
16257c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
16267c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
16277c478bd9Sstevel@tonic-gate 	nfs_argop4 *argop;
16287c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
16297c478bd9Sstevel@tonic-gate 	int num_argops;
16307c478bd9Sstevel@tonic-gate 	lookup4_param_t lookuparg;
16317c478bd9Sstevel@tonic-gate 	nfs_fh4 *tmpfhp;
16327c478bd9Sstevel@tonic-gate 	int doqueue = 1;
16337c478bd9Sstevel@tonic-gate 	char *path;
16347c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
16357c478bd9Sstevel@tonic-gate 
16367c478bd9Sstevel@tonic-gate 	ASSERT(fname != NULL);
16377c478bd9Sstevel@tonic-gate 	ASSERT(rootvp->v_type == VDIR);
16387c478bd9Sstevel@tonic-gate 
16397c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rootvp);
16407c478bd9Sstevel@tonic-gate 	path = fn_path(fname);
16417c478bd9Sstevel@tonic-gate 	switch (filetype) {
16427c478bd9Sstevel@tonic-gate 	case RML_NAMED_ATTR:
16437c478bd9Sstevel@tonic-gate 		lookuparg.l4_getattrs = LKP4_LAST_NAMED_ATTR;
16447c478bd9Sstevel@tonic-gate 		args.ctag = TAG_REMAP_LOOKUP_NA;
16457c478bd9Sstevel@tonic-gate 		break;
16467c478bd9Sstevel@tonic-gate 	case RML_ATTRDIR:
16477c478bd9Sstevel@tonic-gate 		lookuparg.l4_getattrs = LKP4_LAST_ATTRDIR;
16487c478bd9Sstevel@tonic-gate 		args.ctag = TAG_REMAP_LOOKUP_AD;
16497c478bd9Sstevel@tonic-gate 		break;
16507c478bd9Sstevel@tonic-gate 	case RML_ORDINARY:
16517c478bd9Sstevel@tonic-gate 		lookuparg.l4_getattrs = LKP4_ALL_ATTRIBUTES;
16527c478bd9Sstevel@tonic-gate 		args.ctag = TAG_REMAP_LOOKUP;
16537c478bd9Sstevel@tonic-gate 		break;
16547c478bd9Sstevel@tonic-gate 	default:
16557c478bd9Sstevel@tonic-gate 		ep->error = EINVAL;
16567c478bd9Sstevel@tonic-gate 		return;
16577c478bd9Sstevel@tonic-gate 	}
16587c478bd9Sstevel@tonic-gate 	lookuparg.argsp = &args;
16597c478bd9Sstevel@tonic-gate 	lookuparg.resp = &res;
16607c478bd9Sstevel@tonic-gate 	lookuparg.header_len = 1;	/* Putfh */
16617c478bd9Sstevel@tonic-gate 	lookuparg.trailer_len = 0;
16627c478bd9Sstevel@tonic-gate 	lookuparg.ga_bits = NFS4_VATTR_MASK;
16637c478bd9Sstevel@tonic-gate 	lookuparg.mi = VTOMI4(rootvp);
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 	(void) nfs4lookup_setup(path, &lookuparg, 1);
16667c478bd9Sstevel@tonic-gate 
16677c478bd9Sstevel@tonic-gate 	/* 0: putfh directory */
16687c478bd9Sstevel@tonic-gate 	argop = args.array;
16697c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_CPUTFH;
16707c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opcputfh.sfh = VTOR4(rootvp)->r_fh;
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 	num_argops = args.array_len;
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, ep);
16757c478bd9Sstevel@tonic-gate 
16767c478bd9Sstevel@tonic-gate 	if (ep->error || res.status != NFS4_OK)
16777c478bd9Sstevel@tonic-gate 		goto exit;
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	/* get the object filehandle */
16807c478bd9Sstevel@tonic-gate 	resop = &res.array[res.array_len - 2];
16817c478bd9Sstevel@tonic-gate 	if (resop->resop != OP_GETFH) {
16827c478bd9Sstevel@tonic-gate 		nfs4_queue_event(RE_FAIL_REMAP_OP, mi, NULL,
16837c478bd9Sstevel@tonic-gate 		    0, NULL, NULL, 0, NULL, 0, TAG_NONE, TAG_NONE, 0, 0);
16847c478bd9Sstevel@tonic-gate 		ep->stat = NFS4ERR_SERVERFAULT;
16857c478bd9Sstevel@tonic-gate 		goto exit;
16867c478bd9Sstevel@tonic-gate 	}
16877c478bd9Sstevel@tonic-gate 	tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
16887c478bd9Sstevel@tonic-gate 	if (tmpfhp->nfs_fh4_len > NFS4_FHSIZE) {
16897c478bd9Sstevel@tonic-gate 		nfs4_queue_event(RE_FAIL_REMAP_LEN, mi, NULL,
16907c478bd9Sstevel@tonic-gate 		    tmpfhp->nfs_fh4_len, NULL, NULL, 0, NULL, 0, TAG_NONE,
16917c478bd9Sstevel@tonic-gate 		    TAG_NONE, 0, 0);
16927c478bd9Sstevel@tonic-gate 		ep->stat = NFS4ERR_SERVERFAULT;
16937c478bd9Sstevel@tonic-gate 		goto exit;
16947c478bd9Sstevel@tonic-gate 	}
16957c478bd9Sstevel@tonic-gate 	fhp->nfs_fh4_val = kmem_alloc(tmpfhp->nfs_fh4_len, KM_SLEEP);
16967c478bd9Sstevel@tonic-gate 	nfs_fh4_copy(tmpfhp, fhp);
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate 	/* get the object attributes */
16997c478bd9Sstevel@tonic-gate 	resop = &res.array[res.array_len - 1];
17007c478bd9Sstevel@tonic-gate 	if (garp && resop->resop == OP_GETATTR)
17017c478bd9Sstevel@tonic-gate 		*garp = resop->nfs_resop4_u.opgetattr.ga_res;
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate 	/* See if there are enough fields in the response for parent info */
17047c478bd9Sstevel@tonic-gate 	if ((int)res.array_len - 5 <= 0)
17057c478bd9Sstevel@tonic-gate 		goto exit;
17067c478bd9Sstevel@tonic-gate 
17077c478bd9Sstevel@tonic-gate 	/* get the parent filehandle */
17087c478bd9Sstevel@tonic-gate 	resop = &res.array[res.array_len - 5];
17097c478bd9Sstevel@tonic-gate 	if (resop->resop != OP_GETFH) {
17107c478bd9Sstevel@tonic-gate 		nfs4_queue_event(RE_FAIL_REMAP_OP, mi, NULL,
17117c478bd9Sstevel@tonic-gate 		    0, NULL, NULL, 0, NULL, 0, TAG_NONE, TAG_NONE, 0, 0);
17127c478bd9Sstevel@tonic-gate 		ep->stat = NFS4ERR_SERVERFAULT;
17137c478bd9Sstevel@tonic-gate 		goto exit;
17147c478bd9Sstevel@tonic-gate 	}
17157c478bd9Sstevel@tonic-gate 	tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
17167c478bd9Sstevel@tonic-gate 	if (tmpfhp->nfs_fh4_len > NFS4_FHSIZE) {
17177c478bd9Sstevel@tonic-gate 		nfs4_queue_event(RE_FAIL_REMAP_LEN, mi, NULL,
17187c478bd9Sstevel@tonic-gate 		    tmpfhp->nfs_fh4_len, NULL, NULL, 0, NULL, 0, TAG_NONE,
17197c478bd9Sstevel@tonic-gate 		    TAG_NONE, 0, 0);
17207c478bd9Sstevel@tonic-gate 		ep->stat = NFS4ERR_SERVERFAULT;
17217c478bd9Sstevel@tonic-gate 		goto exit;
17227c478bd9Sstevel@tonic-gate 	}
17237c478bd9Sstevel@tonic-gate 	pfhp->nfs_fh4_val = kmem_alloc(tmpfhp->nfs_fh4_len, KM_SLEEP);
17247c478bd9Sstevel@tonic-gate 	nfs_fh4_copy(tmpfhp, pfhp);
17257c478bd9Sstevel@tonic-gate 
17267c478bd9Sstevel@tonic-gate 	/* get the parent attributes */
17277c478bd9Sstevel@tonic-gate 	resop = &res.array[res.array_len - 4];
17287c478bd9Sstevel@tonic-gate 	if (pgarp && resop->resop == OP_GETATTR)
17297c478bd9Sstevel@tonic-gate 		*pgarp = resop->nfs_resop4_u.opgetattr.ga_res;
17307c478bd9Sstevel@tonic-gate 
17317c478bd9Sstevel@tonic-gate exit:
17327c478bd9Sstevel@tonic-gate 	/*
17337c478bd9Sstevel@tonic-gate 	 * It is too hard to remember where all the OP_LOOKUPs are
17347c478bd9Sstevel@tonic-gate 	 */
17357c478bd9Sstevel@tonic-gate 	nfs4args_lookup_free(argop, num_argops);
17367c478bd9Sstevel@tonic-gate 	kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
17377c478bd9Sstevel@tonic-gate 
17387c478bd9Sstevel@tonic-gate 	if (!ep->error)
17397c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
17407c478bd9Sstevel@tonic-gate 	kmem_free(path, strlen(path)+1);
17417c478bd9Sstevel@tonic-gate }
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate /*
17447c478bd9Sstevel@tonic-gate  * NFS client failover / volatile filehandle support
17457c478bd9Sstevel@tonic-gate  *
17467c478bd9Sstevel@tonic-gate  * Recover the filehandle for the given rnode.
17477c478bd9Sstevel@tonic-gate  *
17487c478bd9Sstevel@tonic-gate  * Errors are returned via the nfs4_error_t parameter.
17497c478bd9Sstevel@tonic-gate  */
17507c478bd9Sstevel@tonic-gate 
17517c478bd9Sstevel@tonic-gate void
17527c478bd9Sstevel@tonic-gate nfs4_remap_file(mntinfo4_t *mi, vnode_t *vp, int flags, nfs4_error_t *ep)
17537c478bd9Sstevel@tonic-gate {
17547c478bd9Sstevel@tonic-gate 	rnode4_t *rp = VTOR4(vp);
17557c478bd9Sstevel@tonic-gate 	vnode_t *rootvp = NULL;
17567c478bd9Sstevel@tonic-gate 	vnode_t *dvp = NULL;
17577c478bd9Sstevel@tonic-gate 	cred_t *cr, *cred_otw;
17587c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t gar, pgar;
17597c478bd9Sstevel@tonic-gate 	nfs_fh4 newfh = {0, NULL}, newpfh = {0, NULL};
17607c478bd9Sstevel@tonic-gate 	int filetype = RML_ORDINARY;
17617c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov = {NULL, 0, 0};
17627c478bd9Sstevel@tonic-gate 	int badfhcount = 0;
17637c478bd9Sstevel@tonic-gate 	nfs4_open_stream_t *osp = NULL;
17647c478bd9Sstevel@tonic-gate 	bool_t first_time = TRUE;	/* first time getting OTW cred */
17657c478bd9Sstevel@tonic-gate 	bool_t last_time = FALSE;	/* last time getting OTW cred */
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
17687c478bd9Sstevel@tonic-gate 	    "nfs4_remap_file: remapping %s", rnode4info(rp)));
17697c478bd9Sstevel@tonic-gate 	ASSERT(nfs4_consistent_type(vp));
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 	if (vp->v_flag & VROOT) {
17727c478bd9Sstevel@tonic-gate 		nfs4_remap_root(mi, ep, flags);
17737c478bd9Sstevel@tonic-gate 		return;
17747c478bd9Sstevel@tonic-gate 	}
17757c478bd9Sstevel@tonic-gate 
17767c478bd9Sstevel@tonic-gate 	/*
17777c478bd9Sstevel@tonic-gate 	 * Given the root fh, use the path stored in
17787c478bd9Sstevel@tonic-gate 	 * the rnode to find the fh for the new server.
17797c478bd9Sstevel@tonic-gate 	 */
17807c478bd9Sstevel@tonic-gate 	ep->error = VFS_ROOT(mi->mi_vfsp, &rootvp);
17817c478bd9Sstevel@tonic-gate 	if (ep->error != 0)
17827c478bd9Sstevel@tonic-gate 		return;
17837c478bd9Sstevel@tonic-gate 
17847c478bd9Sstevel@tonic-gate 	cr = curthread->t_cred;
17857c478bd9Sstevel@tonic-gate 	ASSERT(cr != NULL);
17867c478bd9Sstevel@tonic-gate get_remap_cred:
17877c478bd9Sstevel@tonic-gate 	/*
17887c478bd9Sstevel@tonic-gate 	 * Releases the osp, if it is provided.
17897c478bd9Sstevel@tonic-gate 	 * Puts a hold on the cred_otw and the new osp (if found).
17907c478bd9Sstevel@tonic-gate 	 */
17917c478bd9Sstevel@tonic-gate 	cred_otw = nfs4_get_otw_cred_by_osp(rp, cr, &osp,
17927c478bd9Sstevel@tonic-gate 		&first_time, &last_time);
17937c478bd9Sstevel@tonic-gate 	ASSERT(cred_otw != NULL);
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 	if (rp->r_flags & R4ISXATTR) {
17967c478bd9Sstevel@tonic-gate 		filetype = RML_NAMED_ATTR;
17977c478bd9Sstevel@tonic-gate 		(void) vtodv(vp, &dvp, cred_otw, FALSE);
17987c478bd9Sstevel@tonic-gate 	}
17997c478bd9Sstevel@tonic-gate 
18007c478bd9Sstevel@tonic-gate 	if (vp->v_flag & V_XATTRDIR) {
18017c478bd9Sstevel@tonic-gate 		filetype = RML_ATTRDIR;
18027c478bd9Sstevel@tonic-gate 	}
18037c478bd9Sstevel@tonic-gate 
18047c478bd9Sstevel@tonic-gate 	if (filetype == RML_ORDINARY && rootvp->v_type == VREG) {
18057c478bd9Sstevel@tonic-gate 		/* file mount, doesn't need a remap */
18067c478bd9Sstevel@tonic-gate 		goto done;
18077c478bd9Sstevel@tonic-gate 	}
18087c478bd9Sstevel@tonic-gate 
18097c478bd9Sstevel@tonic-gate again:
18107c478bd9Sstevel@tonic-gate 	remap_lookup(rp->r_svnode.sv_name, rootvp, filetype, cred_otw,
18117c478bd9Sstevel@tonic-gate 			&newfh, &gar, &newpfh, &pgar, ep);
18127c478bd9Sstevel@tonic-gate 
18137c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
18147c478bd9Sstevel@tonic-gate 	    "nfs4_remap_file: remap_lookup returned %d/%d",
18157c478bd9Sstevel@tonic-gate 	    ep->error, ep->stat));
18167c478bd9Sstevel@tonic-gate 
18177c478bd9Sstevel@tonic-gate 	if (last_time == FALSE && ep->error == EACCES) {
18187c478bd9Sstevel@tonic-gate 		crfree(cred_otw);
18197c478bd9Sstevel@tonic-gate 		if (dvp != NULL)
18207c478bd9Sstevel@tonic-gate 			VN_RELE(dvp);
18217c478bd9Sstevel@tonic-gate 		goto get_remap_cred;
18227c478bd9Sstevel@tonic-gate 	}
18237c478bd9Sstevel@tonic-gate 	if (ep->error != 0)
18247c478bd9Sstevel@tonic-gate 		goto done;
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate 	switch (ep->stat) {
18277c478bd9Sstevel@tonic-gate 	case NFS4_OK:
18287c478bd9Sstevel@tonic-gate 		badfhcount = 0;
18297c478bd9Sstevel@tonic-gate 		if (recov.rs_flags & NFS4_RS_DELAY_MSG) {
18307c478bd9Sstevel@tonic-gate 			mutex_enter(&rp->r_statelock);
18317c478bd9Sstevel@tonic-gate 			rp->r_delay_interval = 0;
18327c478bd9Sstevel@tonic-gate 			mutex_exit(&rp->r_statelock);
18337c478bd9Sstevel@tonic-gate 			uprintf("NFS File Available..\n");
18347c478bd9Sstevel@tonic-gate 		}
18357c478bd9Sstevel@tonic-gate 		break;
18367c478bd9Sstevel@tonic-gate 	case NFS4ERR_FHEXPIRED:
18377c478bd9Sstevel@tonic-gate 	case NFS4ERR_BADHANDLE:
18387c478bd9Sstevel@tonic-gate 		/*
18397c478bd9Sstevel@tonic-gate 		 * If we ran into filehandle problems, we should try to
18407c478bd9Sstevel@tonic-gate 		 * remap the root vnode first and hope life gets better.
18417c478bd9Sstevel@tonic-gate 		 * But we need to avoid loops.
18427c478bd9Sstevel@tonic-gate 		 */
18437c478bd9Sstevel@tonic-gate 		if (badfhcount++ > 0)
18447c478bd9Sstevel@tonic-gate 			goto done;
18457c478bd9Sstevel@tonic-gate 		if (newfh.nfs_fh4_len != 0) {
18467c478bd9Sstevel@tonic-gate 			kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len);
18477c478bd9Sstevel@tonic-gate 			newfh.nfs_fh4_len = 0;
18487c478bd9Sstevel@tonic-gate 		}
18497c478bd9Sstevel@tonic-gate 		if (newpfh.nfs_fh4_len != 0) {
18507c478bd9Sstevel@tonic-gate 			kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len);
18517c478bd9Sstevel@tonic-gate 			newpfh.nfs_fh4_len = 0;
18527c478bd9Sstevel@tonic-gate 		}
18537c478bd9Sstevel@tonic-gate 		/* relative path - remap rootvp then retry */
18547c478bd9Sstevel@tonic-gate 		VN_RELE(rootvp);
18557c478bd9Sstevel@tonic-gate 		rootvp = NULL;
18567c478bd9Sstevel@tonic-gate 		nfs4_remap_root(mi, ep, flags);
18577c478bd9Sstevel@tonic-gate 		if (ep->error != 0 || ep->stat != NFS4_OK)
18587c478bd9Sstevel@tonic-gate 			goto done;
18597c478bd9Sstevel@tonic-gate 		ep->error = VFS_ROOT(mi->mi_vfsp, &rootvp);
18607c478bd9Sstevel@tonic-gate 		if (ep->error != 0)
18617c478bd9Sstevel@tonic-gate 			goto done;
18627c478bd9Sstevel@tonic-gate 		goto again;
18637c478bd9Sstevel@tonic-gate 	case NFS4ERR_DELAY:
18647c478bd9Sstevel@tonic-gate 		badfhcount = 0;
18657c478bd9Sstevel@tonic-gate 		nfs4_set_delay_wait(vp);
18667c478bd9Sstevel@tonic-gate 		ep->error = nfs4_wait_for_delay(vp, &recov);
18677c478bd9Sstevel@tonic-gate 		if (ep->error != 0)
18687c478bd9Sstevel@tonic-gate 			goto done;
18697c478bd9Sstevel@tonic-gate 		goto again;
18707c478bd9Sstevel@tonic-gate 	case NFS4ERR_ACCESS:
18717c478bd9Sstevel@tonic-gate 		/* get new cred, try again */
18727c478bd9Sstevel@tonic-gate 		if (last_time == TRUE)
18737c478bd9Sstevel@tonic-gate 			goto done;
18747c478bd9Sstevel@tonic-gate 		if (dvp != NULL)
18757c478bd9Sstevel@tonic-gate 			VN_RELE(dvp);
18767c478bd9Sstevel@tonic-gate 		crfree(cred_otw);
18777c478bd9Sstevel@tonic-gate 		goto get_remap_cred;
18787c478bd9Sstevel@tonic-gate 	default:
18797c478bd9Sstevel@tonic-gate 		goto done;
18807c478bd9Sstevel@tonic-gate 	}
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate 	/*
18837c478bd9Sstevel@tonic-gate 	 * Check on the new and old rnodes before updating;
18847c478bd9Sstevel@tonic-gate 	 * if the vnode type or size changes, issue a warning
18857c478bd9Sstevel@tonic-gate 	 * and mark the file dead.
18867c478bd9Sstevel@tonic-gate 	 */
18877c478bd9Sstevel@tonic-gate 	mutex_enter(&rp->r_statelock);
18887c478bd9Sstevel@tonic-gate 	if (flags & NFS4_REMAP_CKATTRS) {
18897c478bd9Sstevel@tonic-gate 		if (vp->v_type != gar.n4g_va.va_type ||
18907c478bd9Sstevel@tonic-gate 			(vp->v_type != VDIR &&
18917c478bd9Sstevel@tonic-gate 			rp->r_size != gar.n4g_va.va_size)) {
18927c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
18937c478bd9Sstevel@tonic-gate 			    "nfs4_remap_file: size %d vs. %d, type %d vs. %d",
18947c478bd9Sstevel@tonic-gate 			    (int)rp->r_size, (int)gar.n4g_va.va_size,
18957c478bd9Sstevel@tonic-gate 			    vp->v_type, gar.n4g_va.va_type));
18967c478bd9Sstevel@tonic-gate 			mutex_exit(&rp->r_statelock);
18977c478bd9Sstevel@tonic-gate 			nfs4_queue_event(RE_FILE_DIFF, mi,
18987c478bd9Sstevel@tonic-gate 			    rp->r_server->sv_hostname, 0, vp, NULL, 0, NULL, 0,
18997c478bd9Sstevel@tonic-gate 			    TAG_NONE, TAG_NONE, 0, 0);
19007c478bd9Sstevel@tonic-gate 			nfs4_fail_recov(vp, NULL, 0, NFS4_OK);
19017c478bd9Sstevel@tonic-gate 			goto done;
19027c478bd9Sstevel@tonic-gate 		}
19037c478bd9Sstevel@tonic-gate 	}
19047c478bd9Sstevel@tonic-gate 	ASSERT(gar.n4g_va.va_type != VNON);
19057c478bd9Sstevel@tonic-gate 	rp->r_server = mi->mi_curr_serv;
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	if (gar.n4g_fsid_valid) {
19087c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&rp->r_server->sv_lock, RW_READER, 0);
19097c478bd9Sstevel@tonic-gate 		rp->r_srv_fsid = gar.n4g_fsid;
19107c478bd9Sstevel@tonic-gate 		if (FATTR4_FSID_EQ(&gar.n4g_fsid, &rp->r_server->sv_fsid))
19117c478bd9Sstevel@tonic-gate 			rp->r_flags &= ~R4SRVSTUB;
19127c478bd9Sstevel@tonic-gate 		else
19137c478bd9Sstevel@tonic-gate 			rp->r_flags |= R4SRVSTUB;
19147c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&rp->r_server->sv_lock);
19157c478bd9Sstevel@tonic-gate #ifdef DEBUG
19167c478bd9Sstevel@tonic-gate 	} else {
19177c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
19187c478bd9Sstevel@tonic-gate 			"remap_file: fsid attr not provided by server.  rp=%p",
19197c478bd9Sstevel@tonic-gate 			(void *)rp));
19207c478bd9Sstevel@tonic-gate #endif
19217c478bd9Sstevel@tonic-gate 	}
19227c478bd9Sstevel@tonic-gate 	mutex_exit(&rp->r_statelock);
19237c478bd9Sstevel@tonic-gate 	nfs4_attrcache_noinval(vp, &gar, gethrtime()); /* force update */
19247c478bd9Sstevel@tonic-gate 	sfh4_update(rp->r_fh, &newfh);
19257c478bd9Sstevel@tonic-gate 	ASSERT(nfs4_consistent_type(vp));
19267c478bd9Sstevel@tonic-gate 
19277c478bd9Sstevel@tonic-gate 	/*
19287c478bd9Sstevel@tonic-gate 	 * If we got parent info, use it to update the parent
19297c478bd9Sstevel@tonic-gate 	 */
19307c478bd9Sstevel@tonic-gate 	if (newpfh.nfs_fh4_len != 0) {
19317c478bd9Sstevel@tonic-gate 		if (rp->r_svnode.sv_dfh != NULL)
19327c478bd9Sstevel@tonic-gate 			sfh4_update(rp->r_svnode.sv_dfh, &newpfh);
19337c478bd9Sstevel@tonic-gate 		if (dvp != NULL) {
19347c478bd9Sstevel@tonic-gate 			/* force update of attrs */
19357c478bd9Sstevel@tonic-gate 			nfs4_attrcache_noinval(dvp, &pgar, gethrtime());
19367c478bd9Sstevel@tonic-gate 		}
19377c478bd9Sstevel@tonic-gate 	}
19387c478bd9Sstevel@tonic-gate done:
19397c478bd9Sstevel@tonic-gate 	if (newfh.nfs_fh4_len != 0)
19407c478bd9Sstevel@tonic-gate 		kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len);
19417c478bd9Sstevel@tonic-gate 	if (newpfh.nfs_fh4_len != 0)
19427c478bd9Sstevel@tonic-gate 		kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len);
19437c478bd9Sstevel@tonic-gate 	if (cred_otw != NULL)
19447c478bd9Sstevel@tonic-gate 		crfree(cred_otw);
19457c478bd9Sstevel@tonic-gate 	if (rootvp != NULL)
19467c478bd9Sstevel@tonic-gate 		VN_RELE(rootvp);
19477c478bd9Sstevel@tonic-gate 	if (dvp != NULL)
19487c478bd9Sstevel@tonic-gate 		VN_RELE(dvp);
19497c478bd9Sstevel@tonic-gate 	if (osp != NULL)
19507c478bd9Sstevel@tonic-gate 		open_stream_rele(osp, rp);
19517c478bd9Sstevel@tonic-gate }
19527c478bd9Sstevel@tonic-gate 
19537c478bd9Sstevel@tonic-gate /*
19547c478bd9Sstevel@tonic-gate  * Client-side failover support: remap the filehandle for vp if it appears
19557c478bd9Sstevel@tonic-gate  * necessary.  errors are returned via the nfs4_error_t parameter; though,
19567c478bd9Sstevel@tonic-gate  * if there is a problem, we will just try again later.
19577c478bd9Sstevel@tonic-gate  */
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate void
19607c478bd9Sstevel@tonic-gate nfs4_check_remap(mntinfo4_t *mi, vnode_t *vp, int flags, nfs4_error_t *ep)
19617c478bd9Sstevel@tonic-gate {
19627c478bd9Sstevel@tonic-gate 	if (vp == NULL)
19637c478bd9Sstevel@tonic-gate 		return;
19647c478bd9Sstevel@tonic-gate 
19657c478bd9Sstevel@tonic-gate 	if (!(vp->v_vfsp->vfs_flag & VFS_RDONLY))
19667c478bd9Sstevel@tonic-gate 		return;
19677c478bd9Sstevel@tonic-gate 
19687c478bd9Sstevel@tonic-gate 	if (VTOR4(vp)->r_server == mi->mi_curr_serv)
19697c478bd9Sstevel@tonic-gate 		return;
19707c478bd9Sstevel@tonic-gate 
19717c478bd9Sstevel@tonic-gate 	nfs4_remap_file(mi, vp, flags, ep);
19727c478bd9Sstevel@tonic-gate }
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate /*
19757c478bd9Sstevel@tonic-gate  * nfs4_make_dotdot() - find or create a parent vnode of a non-root node.
19767c478bd9Sstevel@tonic-gate  *
19777c478bd9Sstevel@tonic-gate  * Our caller has a filehandle for ".." relative to a particular
19787c478bd9Sstevel@tonic-gate  * directory object.  We want to find or create a parent vnode
19797c478bd9Sstevel@tonic-gate  * with that filehandle and return it.  We can of course create
19807c478bd9Sstevel@tonic-gate  * a vnode from this filehandle, but we need to also make sure
19817c478bd9Sstevel@tonic-gate  * that if ".." is a regular file (i.e. dvp is a V_XATTRDIR)
19827c478bd9Sstevel@tonic-gate  * that we have a parent FH for future reopens as well.  If
19837c478bd9Sstevel@tonic-gate  * we have a remap failure, we won't be able to reopen this
19847c478bd9Sstevel@tonic-gate  * file, but we won't treat that as fatal because a reopen
19857c478bd9Sstevel@tonic-gate  * is at least unlikely.  Someday nfs4_reopen() should look
19867c478bd9Sstevel@tonic-gate  * for a missing parent FH and try a remap to recover from it.
19877c478bd9Sstevel@tonic-gate  *
19887c478bd9Sstevel@tonic-gate  * need_start_op argument indicates whether this function should
19897c478bd9Sstevel@tonic-gate  * do a start_op before calling remap_lookup().  This should
19907c478bd9Sstevel@tonic-gate  * be FALSE, if you are the recovery thread or in an op; otherwise,
19917c478bd9Sstevel@tonic-gate  * set it to TRUE.
19927c478bd9Sstevel@tonic-gate  */
19937c478bd9Sstevel@tonic-gate int
19947c478bd9Sstevel@tonic-gate nfs4_make_dotdot(nfs4_sharedfh_t *fhp, hrtime_t t, vnode_t *dvp,
19957c478bd9Sstevel@tonic-gate     cred_t *cr, vnode_t **vpp, int need_start_op)
19967c478bd9Sstevel@tonic-gate {
19977c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi = VTOMI4(dvp);
19987c478bd9Sstevel@tonic-gate 	nfs4_fname_t *np = NULL, *pnp = NULL;
19997c478bd9Sstevel@tonic-gate 	vnode_t *vp = NULL, *rootvp = NULL;
20007c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
20017c478bd9Sstevel@tonic-gate 	nfs_fh4 newfh = {0, NULL}, newpfh = {0, NULL};
20027c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t gar, pgar;
20037c478bd9Sstevel@tonic-gate 	vattr_t va, pva;
20047c478bd9Sstevel@tonic-gate 	nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
20057c478bd9Sstevel@tonic-gate 	nfs4_sharedfh_t *sfh = NULL, *psfh = NULL;
20067c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate #ifdef DEBUG
20097c478bd9Sstevel@tonic-gate 	/*
20107c478bd9Sstevel@tonic-gate 	 * ensure need_start_op is correct
20117c478bd9Sstevel@tonic-gate 	 */
20127c478bd9Sstevel@tonic-gate 	{
20137c478bd9Sstevel@tonic-gate 		int no_need_start_op = (tsd_get(nfs4_tsd_key) ||
20147c478bd9Sstevel@tonic-gate 		    (curthread == mi->mi_recovthread));
20157c478bd9Sstevel@tonic-gate 		/* C needs a ^^ operator! */
20167c478bd9Sstevel@tonic-gate 		ASSERT(((need_start_op) && (!no_need_start_op)) ||
20177c478bd9Sstevel@tonic-gate 		    ((! need_start_op) && (no_need_start_op)));
20187c478bd9Sstevel@tonic-gate 	}
20197c478bd9Sstevel@tonic-gate #endif
20207c478bd9Sstevel@tonic-gate 	ASSERT(VTOMI4(dvp)->mi_zone == curproc->p_zone);
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_shadow_debug, (CE_NOTE,
20237c478bd9Sstevel@tonic-gate 	    "nfs4_make_dotdot: called with fhp %p, dvp %s", (void *)fhp,
20247c478bd9Sstevel@tonic-gate 	    rnode4info(VTOR4(dvp))));
20257c478bd9Sstevel@tonic-gate 
20267c478bd9Sstevel@tonic-gate 	/*
20277c478bd9Sstevel@tonic-gate 	 * rootvp might be needed eventually. Holding it now will
20287c478bd9Sstevel@tonic-gate 	 * ensure that r4find_unlocked() will find it, if ".." is the root.
20297c478bd9Sstevel@tonic-gate 	 */
20307c478bd9Sstevel@tonic-gate 	e.error = VFS_ROOT(mi->mi_vfsp, &rootvp);
20317c478bd9Sstevel@tonic-gate 	if (e.error != 0)
20327c478bd9Sstevel@tonic-gate 		goto out;
20337c478bd9Sstevel@tonic-gate 	rp = r4find_unlocked(fhp, mi->mi_vfsp);
20347c478bd9Sstevel@tonic-gate 	if (rp != NULL) {
20357c478bd9Sstevel@tonic-gate 		*vpp = RTOV4(rp);
20367c478bd9Sstevel@tonic-gate 		VN_RELE(rootvp);
20377c478bd9Sstevel@tonic-gate 		return (0);
20387c478bd9Sstevel@tonic-gate 	}
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 	/*
20417c478bd9Sstevel@tonic-gate 	 * Since we don't have the rnode, we have to go over the wire.
20427c478bd9Sstevel@tonic-gate 	 * remap_lookup() can get all of the filehandles and attributes
20437c478bd9Sstevel@tonic-gate 	 * we need in one operation.
20447c478bd9Sstevel@tonic-gate 	 */
20457c478bd9Sstevel@tonic-gate 	np = fn_parent(VTOSV(dvp)->sv_name);
20467c478bd9Sstevel@tonic-gate 	ASSERT(np != NULL);
20477c478bd9Sstevel@tonic-gate 
20487c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
20497c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
20507c478bd9Sstevel@tonic-gate recov_retry:
20517c478bd9Sstevel@tonic-gate 	if (need_start_op) {
20527c478bd9Sstevel@tonic-gate 		e.error = nfs4_start_fop(mi, rootvp, NULL, OH_LOOKUP,
20537c478bd9Sstevel@tonic-gate 		    &recov_state, NULL);
20547c478bd9Sstevel@tonic-gate 		if (e.error != 0) {
20557c478bd9Sstevel@tonic-gate 			goto out;
20567c478bd9Sstevel@tonic-gate 		}
20577c478bd9Sstevel@tonic-gate 	}
20587c478bd9Sstevel@tonic-gate 	va.va_type = VNON;
20597c478bd9Sstevel@tonic-gate 	pva.va_type = VNON;
20607c478bd9Sstevel@tonic-gate 	remap_lookup(np, rootvp, RML_ORDINARY, cr,
20617c478bd9Sstevel@tonic-gate 	    &newfh, &gar, &newpfh, &pgar, &e);
20627c478bd9Sstevel@tonic-gate 	if (nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp)) {
20637c478bd9Sstevel@tonic-gate 		if (need_start_op) {
20647c478bd9Sstevel@tonic-gate 			bool_t abort;
20657c478bd9Sstevel@tonic-gate 
20667c478bd9Sstevel@tonic-gate 			abort = nfs4_start_recovery(&e, mi,
20677c478bd9Sstevel@tonic-gate 			    rootvp, NULL, NULL, NULL, OP_LOOKUP, NULL);
20687c478bd9Sstevel@tonic-gate 			if (abort) {
20697c478bd9Sstevel@tonic-gate 				nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
20707c478bd9Sstevel@tonic-gate 				    &recov_state, FALSE);
20717c478bd9Sstevel@tonic-gate 				if (e.error == 0)
20727c478bd9Sstevel@tonic-gate 					e.error = EIO;
20737c478bd9Sstevel@tonic-gate 				goto out;
20747c478bd9Sstevel@tonic-gate 			}
20757c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
20767c478bd9Sstevel@tonic-gate 			    &recov_state, TRUE);
20777c478bd9Sstevel@tonic-gate 			goto recov_retry;
20787c478bd9Sstevel@tonic-gate 		}
20797c478bd9Sstevel@tonic-gate 		if (e.error == 0)
20807c478bd9Sstevel@tonic-gate 			e.error = EIO;
20817c478bd9Sstevel@tonic-gate 		goto out;
20827c478bd9Sstevel@tonic-gate 	}
20837c478bd9Sstevel@tonic-gate 
20847c478bd9Sstevel@tonic-gate 	if (!e.error) {
20857c478bd9Sstevel@tonic-gate 		va = gar.n4g_va;
20867c478bd9Sstevel@tonic-gate 		pva = pgar.n4g_va;
20877c478bd9Sstevel@tonic-gate 	}
20887c478bd9Sstevel@tonic-gate 
20897c478bd9Sstevel@tonic-gate 	if ((e.error != 0) ||
20907c478bd9Sstevel@tonic-gate 	    (va.va_type != VDIR)) {
20917c478bd9Sstevel@tonic-gate 		if (e.error == 0)
20927c478bd9Sstevel@tonic-gate 			e.error = EIO;
20937c478bd9Sstevel@tonic-gate 		goto out;
20947c478bd9Sstevel@tonic-gate 	}
20957c478bd9Sstevel@tonic-gate 
20967c478bd9Sstevel@tonic-gate 	if (e.stat != NFS4_OK) {
20977c478bd9Sstevel@tonic-gate 		e.error = EIO;
20987c478bd9Sstevel@tonic-gate 		goto out;
20997c478bd9Sstevel@tonic-gate 	}
21007c478bd9Sstevel@tonic-gate 
21017c478bd9Sstevel@tonic-gate 	/*
21027c478bd9Sstevel@tonic-gate 	 * It is possible for remap_lookup() to return with no error,
21037c478bd9Sstevel@tonic-gate 	 * but without providing the parent filehandle and attrs.
21047c478bd9Sstevel@tonic-gate 	 */
21057c478bd9Sstevel@tonic-gate 	if (pva.va_type != VDIR) {
21067c478bd9Sstevel@tonic-gate 		/*
21077c478bd9Sstevel@tonic-gate 		 * Call remap_lookup() again, this time with the
21087c478bd9Sstevel@tonic-gate 		 * newpfh and pgar args in the first position.
21097c478bd9Sstevel@tonic-gate 		 */
21107c478bd9Sstevel@tonic-gate 		pnp = fn_parent(np);
21117c478bd9Sstevel@tonic-gate 		if (pnp != NULL) {
21127c478bd9Sstevel@tonic-gate 			remap_lookup(pnp, rootvp, RML_ORDINARY, cr,
21137c478bd9Sstevel@tonic-gate 			    &newpfh, &pgar, NULL, NULL, &e);
21147c478bd9Sstevel@tonic-gate 			if (nfs4_needs_recovery(&e, FALSE,
21157c478bd9Sstevel@tonic-gate 			    mi->mi_vfsp)) {
21167c478bd9Sstevel@tonic-gate 				if (need_start_op) {
21177c478bd9Sstevel@tonic-gate 					bool_t abort;
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate 					abort = nfs4_start_recovery(&e, mi,
21207c478bd9Sstevel@tonic-gate 					    rootvp, NULL, NULL, NULL,
21217c478bd9Sstevel@tonic-gate 					    OP_LOOKUP, NULL);
21227c478bd9Sstevel@tonic-gate 					if (abort) {
21237c478bd9Sstevel@tonic-gate 						nfs4_end_fop(mi, rootvp, NULL,
21247c478bd9Sstevel@tonic-gate 						    OH_LOOKUP, &recov_state,
21257c478bd9Sstevel@tonic-gate 						    FALSE);
21267c478bd9Sstevel@tonic-gate 						if (e.error == 0)
21277c478bd9Sstevel@tonic-gate 							e.error = EIO;
21287c478bd9Sstevel@tonic-gate 						goto out;
21297c478bd9Sstevel@tonic-gate 					}
21307c478bd9Sstevel@tonic-gate 					nfs4_end_fop(mi, rootvp, NULL,
21317c478bd9Sstevel@tonic-gate 					    OH_LOOKUP, &recov_state, TRUE);
21327c478bd9Sstevel@tonic-gate 					goto recov_retry;
21337c478bd9Sstevel@tonic-gate 				}
21347c478bd9Sstevel@tonic-gate 				if (e.error == 0)
21357c478bd9Sstevel@tonic-gate 					e.error = EIO;
21367c478bd9Sstevel@tonic-gate 				goto out;
21377c478bd9Sstevel@tonic-gate 			}
21387c478bd9Sstevel@tonic-gate 
21397c478bd9Sstevel@tonic-gate 			if (e.stat != NFS4_OK) {
21407c478bd9Sstevel@tonic-gate 				e.error = EIO;
21417c478bd9Sstevel@tonic-gate 				goto out;
21427c478bd9Sstevel@tonic-gate 			}
21437c478bd9Sstevel@tonic-gate 		}
21447c478bd9Sstevel@tonic-gate 		if ((pnp == NULL) ||
21457c478bd9Sstevel@tonic-gate 		    (e.error != 0) ||
21467c478bd9Sstevel@tonic-gate 		    (pva.va_type == VNON)) {
21477c478bd9Sstevel@tonic-gate 			if (need_start_op)
21487c478bd9Sstevel@tonic-gate 				nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP,
21497c478bd9Sstevel@tonic-gate 				    &recov_state, FALSE);
21507c478bd9Sstevel@tonic-gate 			if (e.error == 0)
21517c478bd9Sstevel@tonic-gate 				e.error = EIO;
21527c478bd9Sstevel@tonic-gate 			goto out;
21537c478bd9Sstevel@tonic-gate 		}
21547c478bd9Sstevel@tonic-gate 	}
21557c478bd9Sstevel@tonic-gate 	ASSERT(newpfh.nfs_fh4_len != 0);
21567c478bd9Sstevel@tonic-gate 	if (need_start_op)
21577c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP, &recov_state, FALSE);
21587c478bd9Sstevel@tonic-gate 	psfh = sfh4_get(&newpfh, mi);
21597c478bd9Sstevel@tonic-gate 
21607c478bd9Sstevel@tonic-gate 	sfh = sfh4_get(&newfh, mi);
21617c478bd9Sstevel@tonic-gate 	vp = makenfs4node_by_fh(sfh, psfh, &np, &gar, mi, cr, t);
21627c478bd9Sstevel@tonic-gate 
21637c478bd9Sstevel@tonic-gate out:
21647c478bd9Sstevel@tonic-gate 	if (np != NULL)
21657c478bd9Sstevel@tonic-gate 		fn_rele(&np);
21667c478bd9Sstevel@tonic-gate 	if (pnp != NULL)
21677c478bd9Sstevel@tonic-gate 		fn_rele(&pnp);
21687c478bd9Sstevel@tonic-gate 	if (newfh.nfs_fh4_len != 0)
21697c478bd9Sstevel@tonic-gate 		kmem_free(newfh.nfs_fh4_val, newfh.nfs_fh4_len);
21707c478bd9Sstevel@tonic-gate 	if (newpfh.nfs_fh4_len != 0)
21717c478bd9Sstevel@tonic-gate 		kmem_free(newpfh.nfs_fh4_val, newpfh.nfs_fh4_len);
21727c478bd9Sstevel@tonic-gate 	if (sfh != NULL)
21737c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
21747c478bd9Sstevel@tonic-gate 	if (psfh != NULL)
21757c478bd9Sstevel@tonic-gate 		sfh4_rele(&psfh);
21767c478bd9Sstevel@tonic-gate 	if (rootvp != NULL)
21777c478bd9Sstevel@tonic-gate 		VN_RELE(rootvp);
21787c478bd9Sstevel@tonic-gate 	*vpp = vp;
21797c478bd9Sstevel@tonic-gate 	return (e.error);
21807c478bd9Sstevel@tonic-gate }
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate #ifdef DEBUG
21837c478bd9Sstevel@tonic-gate size_t r_path_memuse = 0;
21847c478bd9Sstevel@tonic-gate #endif
21857c478bd9Sstevel@tonic-gate 
21867c478bd9Sstevel@tonic-gate /*
21877c478bd9Sstevel@tonic-gate  * NFS client failover support
21887c478bd9Sstevel@tonic-gate  *
21897c478bd9Sstevel@tonic-gate  * sv4_free() frees the malloc'd portion of a "servinfo_t".
21907c478bd9Sstevel@tonic-gate  */
21917c478bd9Sstevel@tonic-gate void
21927c478bd9Sstevel@tonic-gate sv4_free(servinfo4_t *svp)
21937c478bd9Sstevel@tonic-gate {
21947c478bd9Sstevel@tonic-gate 	servinfo4_t *next;
21957c478bd9Sstevel@tonic-gate 	struct knetconfig *knconf;
21967c478bd9Sstevel@tonic-gate 
21977c478bd9Sstevel@tonic-gate 	while (svp != NULL) {
21987c478bd9Sstevel@tonic-gate 		next = svp->sv_next;
21997c478bd9Sstevel@tonic-gate 		if (svp->sv_dhsec)
22007c478bd9Sstevel@tonic-gate 			sec_clnt_freeinfo(svp->sv_dhsec);
22017c478bd9Sstevel@tonic-gate 		if (svp->sv_secdata)
22027c478bd9Sstevel@tonic-gate 			sec_clnt_freeinfo(svp->sv_secdata);
22037c478bd9Sstevel@tonic-gate 		if (svp->sv_save_secinfo &&
22047c478bd9Sstevel@tonic-gate 				svp->sv_save_secinfo != svp->sv_secinfo)
22057c478bd9Sstevel@tonic-gate 			secinfo_free(svp->sv_save_secinfo);
22067c478bd9Sstevel@tonic-gate 		if (svp->sv_secinfo)
22077c478bd9Sstevel@tonic-gate 			secinfo_free(svp->sv_secinfo);
22087c478bd9Sstevel@tonic-gate 		if (svp->sv_hostname && svp->sv_hostnamelen > 0)
22097c478bd9Sstevel@tonic-gate 			kmem_free(svp->sv_hostname, svp->sv_hostnamelen);
22107c478bd9Sstevel@tonic-gate 		knconf = svp->sv_knconf;
22117c478bd9Sstevel@tonic-gate 		if (knconf != NULL) {
22127c478bd9Sstevel@tonic-gate 			if (knconf->knc_protofmly != NULL)
22137c478bd9Sstevel@tonic-gate 				kmem_free(knconf->knc_protofmly, KNC_STRSIZE);
22147c478bd9Sstevel@tonic-gate 			if (knconf->knc_proto != NULL)
22157c478bd9Sstevel@tonic-gate 				kmem_free(knconf->knc_proto, KNC_STRSIZE);
22167c478bd9Sstevel@tonic-gate 			kmem_free(knconf, sizeof (*knconf));
22177c478bd9Sstevel@tonic-gate 		}
22187c478bd9Sstevel@tonic-gate 		knconf = svp->sv_origknconf;
22197c478bd9Sstevel@tonic-gate 		if (knconf != NULL) {
22207c478bd9Sstevel@tonic-gate 			if (knconf->knc_protofmly != NULL)
22217c478bd9Sstevel@tonic-gate 				kmem_free(knconf->knc_protofmly, KNC_STRSIZE);
22227c478bd9Sstevel@tonic-gate 			if (knconf->knc_proto != NULL)
22237c478bd9Sstevel@tonic-gate 				kmem_free(knconf->knc_proto, KNC_STRSIZE);
22247c478bd9Sstevel@tonic-gate 			kmem_free(knconf, sizeof (*knconf));
22257c478bd9Sstevel@tonic-gate 		}
22267c478bd9Sstevel@tonic-gate 		if (svp->sv_addr.buf != NULL && svp->sv_addr.maxlen != 0)
22277c478bd9Sstevel@tonic-gate 			kmem_free(svp->sv_addr.buf, svp->sv_addr.maxlen);
22287c478bd9Sstevel@tonic-gate 		if (svp->sv_path != NULL) {
22297c478bd9Sstevel@tonic-gate 			kmem_free(svp->sv_path, svp->sv_pathlen);
22307c478bd9Sstevel@tonic-gate 		}
22317c478bd9Sstevel@tonic-gate 		nfs_rw_destroy(&svp->sv_lock);
22327c478bd9Sstevel@tonic-gate 		kmem_free(svp, sizeof (*svp));
22337c478bd9Sstevel@tonic-gate 		svp = next;
22347c478bd9Sstevel@tonic-gate 	}
22357c478bd9Sstevel@tonic-gate }
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate void
22387c478bd9Sstevel@tonic-gate nfs4_printfhandle(nfs4_fhandle_t *fhp)
22397c478bd9Sstevel@tonic-gate {
22407c478bd9Sstevel@tonic-gate 	int *ip;
22417c478bd9Sstevel@tonic-gate 	char *buf;
22427c478bd9Sstevel@tonic-gate 	size_t bufsize;
22437c478bd9Sstevel@tonic-gate 	char *cp;
22447c478bd9Sstevel@tonic-gate 
22457c478bd9Sstevel@tonic-gate 	/*
22467c478bd9Sstevel@tonic-gate 	 * 13 == "(file handle:"
22477c478bd9Sstevel@tonic-gate 	 * maximum of NFS_FHANDLE / sizeof (*ip) elements in fh_buf times
22487c478bd9Sstevel@tonic-gate 	 *	1 == ' '
22497c478bd9Sstevel@tonic-gate 	 *	8 == maximum strlen of "%x"
22507c478bd9Sstevel@tonic-gate 	 * 3 == ")\n\0"
22517c478bd9Sstevel@tonic-gate 	 */
22527c478bd9Sstevel@tonic-gate 	bufsize = 13 + ((NFS_FHANDLE_LEN / sizeof (*ip)) * (1 + 8)) + 3;
22537c478bd9Sstevel@tonic-gate 	buf = kmem_alloc(bufsize, KM_NOSLEEP);
22547c478bd9Sstevel@tonic-gate 	if (buf == NULL)
22557c478bd9Sstevel@tonic-gate 		return;
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate 	cp = buf;
22587c478bd9Sstevel@tonic-gate 	(void) strcpy(cp, "(file handle:");
22597c478bd9Sstevel@tonic-gate 	while (*cp != '\0')
22607c478bd9Sstevel@tonic-gate 		cp++;
22617c478bd9Sstevel@tonic-gate 	for (ip = (int *)fhp->fh_buf;
22627c478bd9Sstevel@tonic-gate 	    ip < (int *)&fhp->fh_buf[fhp->fh_len];
22637c478bd9Sstevel@tonic-gate 	    ip++) {
22647c478bd9Sstevel@tonic-gate 		(void) sprintf(cp, " %x", *ip);
22657c478bd9Sstevel@tonic-gate 		while (*cp != '\0')
22667c478bd9Sstevel@tonic-gate 			cp++;
22677c478bd9Sstevel@tonic-gate 	}
22687c478bd9Sstevel@tonic-gate 	(void) strcpy(cp, ")\n");
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate 	zcmn_err(getzoneid(), CE_CONT, "%s", buf);
22717c478bd9Sstevel@tonic-gate 
22727c478bd9Sstevel@tonic-gate 	kmem_free(buf, bufsize);
22737c478bd9Sstevel@tonic-gate }
22747c478bd9Sstevel@tonic-gate 
22757c478bd9Sstevel@tonic-gate /*
22767c478bd9Sstevel@tonic-gate  * The NFSv4 readdir cache subsystem.
22777c478bd9Sstevel@tonic-gate  *
22787c478bd9Sstevel@tonic-gate  * We provide a set of interfaces to allow the rest of the system to utilize
22797c478bd9Sstevel@tonic-gate  * a caching mechanism while encapsulating the details of the actual
22807c478bd9Sstevel@tonic-gate  * implementation.  This should allow for better maintainability and
22817c478bd9Sstevel@tonic-gate  * extensibilty by consolidating the implementation details in one location.
22827c478bd9Sstevel@tonic-gate  */
22837c478bd9Sstevel@tonic-gate 
22847c478bd9Sstevel@tonic-gate /*
22857c478bd9Sstevel@tonic-gate  * Comparator used by AVL routines.
22867c478bd9Sstevel@tonic-gate  */
22877c478bd9Sstevel@tonic-gate static int
22887c478bd9Sstevel@tonic-gate rddir4_cache_compar(const void *x, const void *y)
22897c478bd9Sstevel@tonic-gate {
22907c478bd9Sstevel@tonic-gate 	rddir4_cache_impl *ai = (rddir4_cache_impl *)x;
22917c478bd9Sstevel@tonic-gate 	rddir4_cache_impl *bi = (rddir4_cache_impl *)y;
22927c478bd9Sstevel@tonic-gate 	rddir4_cache *a = &ai->rc;
22937c478bd9Sstevel@tonic-gate 	rddir4_cache *b = &bi->rc;
22947c478bd9Sstevel@tonic-gate 
22957c478bd9Sstevel@tonic-gate 	if (a->nfs4_cookie == b->nfs4_cookie) {
22967c478bd9Sstevel@tonic-gate 		if (a->buflen == b->buflen)
22977c478bd9Sstevel@tonic-gate 			return (0);
22987c478bd9Sstevel@tonic-gate 		if (a->buflen < b->buflen)
22997c478bd9Sstevel@tonic-gate 			return (-1);
23007c478bd9Sstevel@tonic-gate 		return (1);
23017c478bd9Sstevel@tonic-gate 	}
23027c478bd9Sstevel@tonic-gate 
23037c478bd9Sstevel@tonic-gate 	if (a->nfs4_cookie < b->nfs4_cookie)
23047c478bd9Sstevel@tonic-gate 			return (-1);
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate 	return (1);
23077c478bd9Sstevel@tonic-gate }
23087c478bd9Sstevel@tonic-gate 
23097c478bd9Sstevel@tonic-gate /*
23107c478bd9Sstevel@tonic-gate  * Allocate an opaque handle for the readdir cache.
23117c478bd9Sstevel@tonic-gate  */
23127c478bd9Sstevel@tonic-gate void
23137c478bd9Sstevel@tonic-gate rddir4_cache_create(rnode4_t *rp)
23147c478bd9Sstevel@tonic-gate {
23157c478bd9Sstevel@tonic-gate 	ASSERT(rp->r_dir == NULL);
23167c478bd9Sstevel@tonic-gate 
23177c478bd9Sstevel@tonic-gate 	rp->r_dir = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
23187c478bd9Sstevel@tonic-gate 
23197c478bd9Sstevel@tonic-gate 	avl_create(rp->r_dir, rddir4_cache_compar, sizeof (rddir4_cache_impl),
23207c478bd9Sstevel@tonic-gate 			offsetof(rddir4_cache_impl, tree));
23217c478bd9Sstevel@tonic-gate }
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate /*
23247c478bd9Sstevel@tonic-gate  *  Purge the cache of all cached readdir responses.
23257c478bd9Sstevel@tonic-gate  */
23267c478bd9Sstevel@tonic-gate void
23277c478bd9Sstevel@tonic-gate rddir4_cache_purge(rnode4_t *rp)
23287c478bd9Sstevel@tonic-gate {
23297c478bd9Sstevel@tonic-gate 	rddir4_cache_impl	*rdip;
23307c478bd9Sstevel@tonic-gate 	rddir4_cache_impl	*nrdip;
23317c478bd9Sstevel@tonic-gate 
23327c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&rp->r_statelock));
23337c478bd9Sstevel@tonic-gate 
23347c478bd9Sstevel@tonic-gate 	if (rp->r_dir == NULL)
23357c478bd9Sstevel@tonic-gate 		return;
23367c478bd9Sstevel@tonic-gate 
23377c478bd9Sstevel@tonic-gate 	rdip = avl_first(rp->r_dir);
23387c478bd9Sstevel@tonic-gate 
23397c478bd9Sstevel@tonic-gate 	while (rdip != NULL) {
23407c478bd9Sstevel@tonic-gate 		nrdip = AVL_NEXT(rp->r_dir, rdip);
23417c478bd9Sstevel@tonic-gate 		avl_remove(rp->r_dir, rdip);
23427c478bd9Sstevel@tonic-gate 		rdip->rc.flags &= ~RDDIRCACHED;
23437c478bd9Sstevel@tonic-gate 		rddir4_cache_rele(rp, &rdip->rc);
23447c478bd9Sstevel@tonic-gate 		rdip = nrdip;
23457c478bd9Sstevel@tonic-gate 	}
23467c478bd9Sstevel@tonic-gate 	ASSERT(avl_numnodes(rp->r_dir) == 0);
23477c478bd9Sstevel@tonic-gate }
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate /*
23507c478bd9Sstevel@tonic-gate  * Destroy the readdir cache.
23517c478bd9Sstevel@tonic-gate  */
23527c478bd9Sstevel@tonic-gate void
23537c478bd9Sstevel@tonic-gate rddir4_cache_destroy(rnode4_t *rp)
23547c478bd9Sstevel@tonic-gate {
23557c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&rp->r_statelock));
23567c478bd9Sstevel@tonic-gate 	if (rp->r_dir == NULL)
23577c478bd9Sstevel@tonic-gate 		return;
23587c478bd9Sstevel@tonic-gate 
23597c478bd9Sstevel@tonic-gate 	rddir4_cache_purge(rp);
23607c478bd9Sstevel@tonic-gate 	avl_destroy(rp->r_dir);
23617c478bd9Sstevel@tonic-gate 	kmem_free(rp->r_dir, sizeof (avl_tree_t));
23627c478bd9Sstevel@tonic-gate 	rp->r_dir = NULL;
23637c478bd9Sstevel@tonic-gate }
23647c478bd9Sstevel@tonic-gate 
23657c478bd9Sstevel@tonic-gate /*
23667c478bd9Sstevel@tonic-gate  * Locate a readdir response from the readdir cache.
23677c478bd9Sstevel@tonic-gate  *
23687c478bd9Sstevel@tonic-gate  * Return values:
23697c478bd9Sstevel@tonic-gate  *
23707c478bd9Sstevel@tonic-gate  * NULL - If there is an unrecoverable situation like the operation may have
23717c478bd9Sstevel@tonic-gate  *	  been interrupted.
23727c478bd9Sstevel@tonic-gate  *
23737c478bd9Sstevel@tonic-gate  * rddir4_cache * - A pointer to a rddir4_cache is returned to the caller.
23747c478bd9Sstevel@tonic-gate  *		    The flags are set approprately, such that the caller knows
23757c478bd9Sstevel@tonic-gate  *		    what state the entry is in.
23767c478bd9Sstevel@tonic-gate  */
23777c478bd9Sstevel@tonic-gate rddir4_cache *
23787c478bd9Sstevel@tonic-gate rddir4_cache_lookup(rnode4_t *rp, offset_t cookie, int count)
23797c478bd9Sstevel@tonic-gate {
23807c478bd9Sstevel@tonic-gate 	rddir4_cache_impl	*rdip = NULL;
23817c478bd9Sstevel@tonic-gate 	rddir4_cache_impl	srdip;
23827c478bd9Sstevel@tonic-gate 	rddir4_cache		*srdc;
23837c478bd9Sstevel@tonic-gate 	rddir4_cache		*rdc = NULL;
23847c478bd9Sstevel@tonic-gate 	rddir4_cache		*nrdc = NULL;
23857c478bd9Sstevel@tonic-gate 	avl_index_t		where;
23867c478bd9Sstevel@tonic-gate 
23877c478bd9Sstevel@tonic-gate top:
23887c478bd9Sstevel@tonic-gate 	ASSERT(nfs_rw_lock_held(&rp->r_rwlock, RW_READER));
23897c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&rp->r_statelock));
23907c478bd9Sstevel@tonic-gate 	/*
23917c478bd9Sstevel@tonic-gate 	 * Check to see if the readdir cache has been disabled.  If so, then
23927c478bd9Sstevel@tonic-gate 	 * simply allocate an rddir4_cache entry and return it, since caching
23937c478bd9Sstevel@tonic-gate 	 * operations do not apply.
23947c478bd9Sstevel@tonic-gate 	 */
23957c478bd9Sstevel@tonic-gate 	if (rp->r_dir == NULL) {
23967c478bd9Sstevel@tonic-gate 		if (nrdc == NULL) {
23977c478bd9Sstevel@tonic-gate 			/*
23987c478bd9Sstevel@tonic-gate 			 * Drop the lock because we are doing a sleeping
23997c478bd9Sstevel@tonic-gate 			 * allocation.
24007c478bd9Sstevel@tonic-gate 			 */
24017c478bd9Sstevel@tonic-gate 			mutex_exit(&rp->r_statelock);
24027c478bd9Sstevel@tonic-gate 			rdc = rddir4_cache_alloc(KM_SLEEP);
24037c478bd9Sstevel@tonic-gate 			rdc->nfs4_cookie = cookie;
24047c478bd9Sstevel@tonic-gate 			rdc->buflen = count;
24057c478bd9Sstevel@tonic-gate 			mutex_enter(&rp->r_statelock);
24067c478bd9Sstevel@tonic-gate 			return (rdc);
24077c478bd9Sstevel@tonic-gate 		}
24087c478bd9Sstevel@tonic-gate 		return (nrdc);
24097c478bd9Sstevel@tonic-gate 	}
24107c478bd9Sstevel@tonic-gate 
24117c478bd9Sstevel@tonic-gate 	srdc = &srdip.rc;
24127c478bd9Sstevel@tonic-gate 	srdc->nfs4_cookie = cookie;
24137c478bd9Sstevel@tonic-gate 	srdc->buflen = count;
24147c478bd9Sstevel@tonic-gate 
24157c478bd9Sstevel@tonic-gate 	rdip = avl_find(rp->r_dir, &srdip, &where);
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate 	/*
24187c478bd9Sstevel@tonic-gate 	 * If we didn't find an entry then create one and insert it
24197c478bd9Sstevel@tonic-gate 	 * into the cache.
24207c478bd9Sstevel@tonic-gate 	 */
24217c478bd9Sstevel@tonic-gate 	if (rdip == NULL) {
24227c478bd9Sstevel@tonic-gate 		/*
24237c478bd9Sstevel@tonic-gate 		 * Check for the case where we have made a second pass through
24247c478bd9Sstevel@tonic-gate 		 * the cache due to a lockless allocation.  If we find that no
24257c478bd9Sstevel@tonic-gate 		 * thread has already inserted this entry, do the insert now
24267c478bd9Sstevel@tonic-gate 		 * and return.
24277c478bd9Sstevel@tonic-gate 		 */
24287c478bd9Sstevel@tonic-gate 		if (nrdc != NULL) {
24297c478bd9Sstevel@tonic-gate 			avl_insert(rp->r_dir, nrdc->data, where);
24307c478bd9Sstevel@tonic-gate 			nrdc->flags |= RDDIRCACHED;
24317c478bd9Sstevel@tonic-gate 			rddir4_cache_hold(nrdc);
24327c478bd9Sstevel@tonic-gate 			return (nrdc);
24337c478bd9Sstevel@tonic-gate 		}
24347c478bd9Sstevel@tonic-gate 
24357c478bd9Sstevel@tonic-gate #ifdef DEBUG
24367c478bd9Sstevel@tonic-gate 		nfs4_readdir_cache_misses++;
24377c478bd9Sstevel@tonic-gate #endif
24387c478bd9Sstevel@tonic-gate 		/*
24397c478bd9Sstevel@tonic-gate 		 * First, try to allocate an entry without sleeping.  If that
24407c478bd9Sstevel@tonic-gate 		 * fails then drop the lock and do a sleeping allocation.
24417c478bd9Sstevel@tonic-gate 		 */
24427c478bd9Sstevel@tonic-gate 		nrdc = rddir4_cache_alloc(KM_NOSLEEP);
24437c478bd9Sstevel@tonic-gate 		if (nrdc != NULL) {
24447c478bd9Sstevel@tonic-gate 			nrdc->nfs4_cookie = cookie;
24457c478bd9Sstevel@tonic-gate 			nrdc->buflen = count;
24467c478bd9Sstevel@tonic-gate 			avl_insert(rp->r_dir, nrdc->data, where);
24477c478bd9Sstevel@tonic-gate 			nrdc->flags |= RDDIRCACHED;
24487c478bd9Sstevel@tonic-gate 			rddir4_cache_hold(nrdc);
24497c478bd9Sstevel@tonic-gate 			return (nrdc);
24507c478bd9Sstevel@tonic-gate 		}
24517c478bd9Sstevel@tonic-gate 
24527c478bd9Sstevel@tonic-gate 		/*
24537c478bd9Sstevel@tonic-gate 		 * Drop the lock and do a sleeping allocation.	We incur
24547c478bd9Sstevel@tonic-gate 		 * additional overhead by having to search the cache again,
24557c478bd9Sstevel@tonic-gate 		 * but this case should be rare.
24567c478bd9Sstevel@tonic-gate 		 */
24577c478bd9Sstevel@tonic-gate 		mutex_exit(&rp->r_statelock);
24587c478bd9Sstevel@tonic-gate 		nrdc = rddir4_cache_alloc(KM_SLEEP);
24597c478bd9Sstevel@tonic-gate 		nrdc->nfs4_cookie = cookie;
24607c478bd9Sstevel@tonic-gate 		nrdc->buflen = count;
24617c478bd9Sstevel@tonic-gate 		mutex_enter(&rp->r_statelock);
24627c478bd9Sstevel@tonic-gate 		/*
24637c478bd9Sstevel@tonic-gate 		 * We need to take another pass through the cache
24647c478bd9Sstevel@tonic-gate 		 * since we dropped our lock to perform the alloc.
24657c478bd9Sstevel@tonic-gate 		 * Another thread may have come by and inserted the
24667c478bd9Sstevel@tonic-gate 		 * entry we are interested in.
24677c478bd9Sstevel@tonic-gate 		 */
24687c478bd9Sstevel@tonic-gate 		goto top;
24697c478bd9Sstevel@tonic-gate 	}
24707c478bd9Sstevel@tonic-gate 
24717c478bd9Sstevel@tonic-gate 	/*
24727c478bd9Sstevel@tonic-gate 	 * Check to see if we need to free our entry.  This can happen if
24737c478bd9Sstevel@tonic-gate 	 * another thread came along beat us to the insert.  We can
24747c478bd9Sstevel@tonic-gate 	 * safely call rddir4_cache_free directly because no other thread
24757c478bd9Sstevel@tonic-gate 	 * would have a reference to this entry.
24767c478bd9Sstevel@tonic-gate 	 */
24777c478bd9Sstevel@tonic-gate 	if (nrdc != NULL)
24787c478bd9Sstevel@tonic-gate 		rddir4_cache_free((rddir4_cache_impl *)nrdc->data);
24797c478bd9Sstevel@tonic-gate 
24807c478bd9Sstevel@tonic-gate #ifdef DEBUG
24817c478bd9Sstevel@tonic-gate 	nfs4_readdir_cache_hits++;
24827c478bd9Sstevel@tonic-gate #endif
24837c478bd9Sstevel@tonic-gate 	/*
24847c478bd9Sstevel@tonic-gate 	 * Found something.  Make sure it's ready to return.
24857c478bd9Sstevel@tonic-gate 	 */
24867c478bd9Sstevel@tonic-gate 	rdc = &rdip->rc;
24877c478bd9Sstevel@tonic-gate 	rddir4_cache_hold(rdc);
24887c478bd9Sstevel@tonic-gate 	/*
24897c478bd9Sstevel@tonic-gate 	 * If the cache entry is in the process of being filled in, wait
24907c478bd9Sstevel@tonic-gate 	 * until this completes.  The RDDIRWAIT bit is set to indicate that
24917c478bd9Sstevel@tonic-gate 	 * someone is waiting and when the thread currently filling the entry
24927c478bd9Sstevel@tonic-gate 	 * is done, it should do a cv_broadcast to wakeup all of the threads
24937c478bd9Sstevel@tonic-gate 	 * waiting for it to finish. If the thread wakes up to find that
24947c478bd9Sstevel@tonic-gate 	 * someone new is now trying to complete the the entry, go back
24957c478bd9Sstevel@tonic-gate 	 * to sleep.
24967c478bd9Sstevel@tonic-gate 	 */
24977c478bd9Sstevel@tonic-gate 	while (rdc->flags & RDDIR) {
24987c478bd9Sstevel@tonic-gate 		/*
24997c478bd9Sstevel@tonic-gate 		 * The entry is not complete.
25007c478bd9Sstevel@tonic-gate 		 */
25017c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&rp->r_rwlock);
25027c478bd9Sstevel@tonic-gate 		rdc->flags |= RDDIRWAIT;
25037c478bd9Sstevel@tonic-gate #ifdef DEBUG
25047c478bd9Sstevel@tonic-gate 		nfs4_readdir_cache_waits++;
25057c478bd9Sstevel@tonic-gate #endif
25067c478bd9Sstevel@tonic-gate 		while (rdc->flags & RDDIRWAIT) {
25077c478bd9Sstevel@tonic-gate 			if (!cv_wait_sig(&rdc->cv, &rp->r_statelock)) {
25087c478bd9Sstevel@tonic-gate 				/*
25097c478bd9Sstevel@tonic-gate 				 * We got interrupted, probably the user
25107c478bd9Sstevel@tonic-gate 				 * typed ^C or an alarm fired.  We free the
25117c478bd9Sstevel@tonic-gate 				 * new entry if we allocated one.
25127c478bd9Sstevel@tonic-gate 				 */
25137c478bd9Sstevel@tonic-gate 				rddir4_cache_rele(rp, rdc);
25147c478bd9Sstevel@tonic-gate 				mutex_exit(&rp->r_statelock);
25157c478bd9Sstevel@tonic-gate 				(void) nfs_rw_enter_sig(&rp->r_rwlock,
25167c478bd9Sstevel@tonic-gate 					RW_READER, FALSE);
25177c478bd9Sstevel@tonic-gate 				mutex_enter(&rp->r_statelock);
25187c478bd9Sstevel@tonic-gate 				return (NULL);
25197c478bd9Sstevel@tonic-gate 			}
25207c478bd9Sstevel@tonic-gate 		}
25217c478bd9Sstevel@tonic-gate 		mutex_exit(&rp->r_statelock);
25227c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&rp->r_rwlock,
25237c478bd9Sstevel@tonic-gate 			RW_READER, FALSE);
25247c478bd9Sstevel@tonic-gate 		mutex_enter(&rp->r_statelock);
25257c478bd9Sstevel@tonic-gate 	}
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 	/*
25287c478bd9Sstevel@tonic-gate 	 * The entry we were waiting on may have been purged from
25297c478bd9Sstevel@tonic-gate 	 * the cache and should no longer be used, release it and
25307c478bd9Sstevel@tonic-gate 	 * start over.
25317c478bd9Sstevel@tonic-gate 	 */
25327c478bd9Sstevel@tonic-gate 	if (!(rdc->flags & RDDIRCACHED)) {
25337c478bd9Sstevel@tonic-gate 		rddir4_cache_rele(rp, rdc);
25347c478bd9Sstevel@tonic-gate 		goto top;
25357c478bd9Sstevel@tonic-gate 	}
25367c478bd9Sstevel@tonic-gate 
25377c478bd9Sstevel@tonic-gate 	/*
25387c478bd9Sstevel@tonic-gate 	 * The entry is completed.  Return it.
25397c478bd9Sstevel@tonic-gate 	 */
25407c478bd9Sstevel@tonic-gate 	return (rdc);
25417c478bd9Sstevel@tonic-gate }
25427c478bd9Sstevel@tonic-gate 
25437c478bd9Sstevel@tonic-gate /*
25447c478bd9Sstevel@tonic-gate  * Allocate a cache element and return it.  Can return NULL if memory is
25457c478bd9Sstevel@tonic-gate  * low.
25467c478bd9Sstevel@tonic-gate  */
25477c478bd9Sstevel@tonic-gate static rddir4_cache *
25487c478bd9Sstevel@tonic-gate rddir4_cache_alloc(int flags)
25497c478bd9Sstevel@tonic-gate {
25507c478bd9Sstevel@tonic-gate 	rddir4_cache_impl	*rdip = NULL;
25517c478bd9Sstevel@tonic-gate 	rddir4_cache		*rc = NULL;
25527c478bd9Sstevel@tonic-gate 
25537c478bd9Sstevel@tonic-gate 	rdip = kmem_alloc(sizeof (rddir4_cache_impl), flags);
25547c478bd9Sstevel@tonic-gate 
25557c478bd9Sstevel@tonic-gate 	if (rdip != NULL) {
25567c478bd9Sstevel@tonic-gate 		rc = &rdip->rc;
25577c478bd9Sstevel@tonic-gate 		rc->data = (void *)rdip;
25587c478bd9Sstevel@tonic-gate 		rc->nfs4_cookie = 0;
25597c478bd9Sstevel@tonic-gate 		rc->nfs4_ncookie = 0;
25607c478bd9Sstevel@tonic-gate 		rc->entries = NULL;
25617c478bd9Sstevel@tonic-gate 		rc->eof = 0;
25627c478bd9Sstevel@tonic-gate 		rc->entlen = 0;
25637c478bd9Sstevel@tonic-gate 		rc->buflen = 0;
25647c478bd9Sstevel@tonic-gate 		rc->actlen = 0;
25657c478bd9Sstevel@tonic-gate 		/*
25667c478bd9Sstevel@tonic-gate 		 * A readdir is required so set the flag.
25677c478bd9Sstevel@tonic-gate 		 */
25687c478bd9Sstevel@tonic-gate 		rc->flags = RDDIRREQ;
25697c478bd9Sstevel@tonic-gate 		cv_init(&rc->cv, NULL, CV_DEFAULT, NULL);
25707c478bd9Sstevel@tonic-gate 		rc->error = 0;
25717c478bd9Sstevel@tonic-gate 		mutex_init(&rdip->lock, NULL, MUTEX_DEFAULT, NULL);
25727c478bd9Sstevel@tonic-gate 		rdip->count = 1;
25737c478bd9Sstevel@tonic-gate #ifdef DEBUG
25747c478bd9Sstevel@tonic-gate 		atomic_add_64(&clstat4_debug.dirent.value.ui64, 1);
25757c478bd9Sstevel@tonic-gate #endif
25767c478bd9Sstevel@tonic-gate 	}
25777c478bd9Sstevel@tonic-gate 	return (rc);
25787c478bd9Sstevel@tonic-gate }
25797c478bd9Sstevel@tonic-gate 
25807c478bd9Sstevel@tonic-gate /*
25817c478bd9Sstevel@tonic-gate  * Increment the reference count to this cache element.
25827c478bd9Sstevel@tonic-gate  */
25837c478bd9Sstevel@tonic-gate static void
25847c478bd9Sstevel@tonic-gate rddir4_cache_hold(rddir4_cache *rc)
25857c478bd9Sstevel@tonic-gate {
25867c478bd9Sstevel@tonic-gate 	rddir4_cache_impl *rdip = (rddir4_cache_impl *)rc->data;
25877c478bd9Sstevel@tonic-gate 
25887c478bd9Sstevel@tonic-gate 	mutex_enter(&rdip->lock);
25897c478bd9Sstevel@tonic-gate 	rdip->count++;
25907c478bd9Sstevel@tonic-gate 	mutex_exit(&rdip->lock);
25917c478bd9Sstevel@tonic-gate }
25927c478bd9Sstevel@tonic-gate 
25937c478bd9Sstevel@tonic-gate /*
25947c478bd9Sstevel@tonic-gate  * Release a reference to this cache element.  If the count is zero then
25957c478bd9Sstevel@tonic-gate  * free the element.
25967c478bd9Sstevel@tonic-gate  */
25977c478bd9Sstevel@tonic-gate void
25987c478bd9Sstevel@tonic-gate rddir4_cache_rele(rnode4_t *rp, rddir4_cache *rdc)
25997c478bd9Sstevel@tonic-gate {
26007c478bd9Sstevel@tonic-gate 	rddir4_cache_impl *rdip = (rddir4_cache_impl *)rdc->data;
26017c478bd9Sstevel@tonic-gate 
26027c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&rp->r_statelock));
26037c478bd9Sstevel@tonic-gate 
26047c478bd9Sstevel@tonic-gate 	/*
26057c478bd9Sstevel@tonic-gate 	 * Check to see if we have any waiters.  If so, we can wake them
26067c478bd9Sstevel@tonic-gate 	 * so that they can proceed.
26077c478bd9Sstevel@tonic-gate 	 */
26087c478bd9Sstevel@tonic-gate 	if (rdc->flags & RDDIRWAIT) {
26097c478bd9Sstevel@tonic-gate 		rdc->flags &= ~RDDIRWAIT;
26107c478bd9Sstevel@tonic-gate 		cv_broadcast(&rdc->cv);
26117c478bd9Sstevel@tonic-gate 	}
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 	mutex_enter(&rdip->lock);
26147c478bd9Sstevel@tonic-gate 	ASSERT(rdip->count > 0);
26157c478bd9Sstevel@tonic-gate 	if (--rdip->count == 0) {
26167c478bd9Sstevel@tonic-gate 		mutex_exit(&rdip->lock);
26177c478bd9Sstevel@tonic-gate 		rddir4_cache_free(rdip);
26187c478bd9Sstevel@tonic-gate 	} else
26197c478bd9Sstevel@tonic-gate 		mutex_exit(&rdip->lock);
26207c478bd9Sstevel@tonic-gate }
26217c478bd9Sstevel@tonic-gate 
26227c478bd9Sstevel@tonic-gate /*
26237c478bd9Sstevel@tonic-gate  * Free a cache element.
26247c478bd9Sstevel@tonic-gate  */
26257c478bd9Sstevel@tonic-gate static void
26267c478bd9Sstevel@tonic-gate rddir4_cache_free(rddir4_cache_impl *rdip)
26277c478bd9Sstevel@tonic-gate {
26287c478bd9Sstevel@tonic-gate 	rddir4_cache *rc = &rdip->rc;
26297c478bd9Sstevel@tonic-gate 
26307c478bd9Sstevel@tonic-gate #ifdef DEBUG
26317c478bd9Sstevel@tonic-gate 	atomic_add_64(&clstat4_debug.dirent.value.ui64, -1);
26327c478bd9Sstevel@tonic-gate #endif
26337c478bd9Sstevel@tonic-gate 	if (rc->entries != NULL)
26347c478bd9Sstevel@tonic-gate 		kmem_free(rc->entries, rc->buflen);
26357c478bd9Sstevel@tonic-gate 	cv_destroy(&rc->cv);
26367c478bd9Sstevel@tonic-gate 	mutex_destroy(&rdip->lock);
26377c478bd9Sstevel@tonic-gate 	kmem_free(rdip, sizeof (*rdip));
26387c478bd9Sstevel@tonic-gate }
26397c478bd9Sstevel@tonic-gate 
26407c478bd9Sstevel@tonic-gate /*
26417c478bd9Sstevel@tonic-gate  * Snapshot callback for nfs:0:nfs4_client as registered with the kstat
26427c478bd9Sstevel@tonic-gate  * framework.
26437c478bd9Sstevel@tonic-gate  */
26447c478bd9Sstevel@tonic-gate static int
26457c478bd9Sstevel@tonic-gate cl4_snapshot(kstat_t *ksp, void *buf, int rw)
26467c478bd9Sstevel@tonic-gate {
26477c478bd9Sstevel@tonic-gate 	ksp->ks_snaptime = gethrtime();
26487c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE) {
26497c478bd9Sstevel@tonic-gate 		bcopy(buf, ksp->ks_private, sizeof (clstat4_tmpl));
26507c478bd9Sstevel@tonic-gate #ifdef DEBUG
26517c478bd9Sstevel@tonic-gate 		/*
26527c478bd9Sstevel@tonic-gate 		 * Currently only the global zone can write to kstats, but we
26537c478bd9Sstevel@tonic-gate 		 * add the check just for paranoia.
26547c478bd9Sstevel@tonic-gate 		 */
26557c478bd9Sstevel@tonic-gate 		if (INGLOBALZONE(curproc))
26567c478bd9Sstevel@tonic-gate 		    bcopy((char *)buf + sizeof (clstat4_tmpl), &clstat4_debug,
26577c478bd9Sstevel@tonic-gate 			    sizeof (clstat4_debug));
26587c478bd9Sstevel@tonic-gate #endif
26597c478bd9Sstevel@tonic-gate 	} else {
26607c478bd9Sstevel@tonic-gate 		bcopy(ksp->ks_private, buf, sizeof (clstat4_tmpl));
26617c478bd9Sstevel@tonic-gate #ifdef DEBUG
26627c478bd9Sstevel@tonic-gate 		/*
26637c478bd9Sstevel@tonic-gate 		 * If we're displaying the "global" debug kstat values, we
26647c478bd9Sstevel@tonic-gate 		 * display them as-is to all zones since in fact they apply to
26657c478bd9Sstevel@tonic-gate 		 * the system as a whole.
26667c478bd9Sstevel@tonic-gate 		 */
26677c478bd9Sstevel@tonic-gate 		bcopy(&clstat4_debug, (char *)buf + sizeof (clstat4_tmpl),
26687c478bd9Sstevel@tonic-gate 		    sizeof (clstat4_debug));
26697c478bd9Sstevel@tonic-gate #endif
26707c478bd9Sstevel@tonic-gate 	}
26717c478bd9Sstevel@tonic-gate 	return (0);
26727c478bd9Sstevel@tonic-gate }
26737c478bd9Sstevel@tonic-gate 
26747c478bd9Sstevel@tonic-gate 
26757c478bd9Sstevel@tonic-gate 
26767c478bd9Sstevel@tonic-gate /*
26777c478bd9Sstevel@tonic-gate  * Zone support
26787c478bd9Sstevel@tonic-gate  */
26797c478bd9Sstevel@tonic-gate static void *
26807c478bd9Sstevel@tonic-gate clinit4_zone(zoneid_t zoneid)
26817c478bd9Sstevel@tonic-gate {
26827c478bd9Sstevel@tonic-gate 	kstat_t *nfs4_client_kstat;
26837c478bd9Sstevel@tonic-gate 	struct nfs4_clnt *nfscl;
26847c478bd9Sstevel@tonic-gate 	uint_t ndata;
26857c478bd9Sstevel@tonic-gate 
26867c478bd9Sstevel@tonic-gate 	nfscl = kmem_alloc(sizeof (*nfscl), KM_SLEEP);
26877c478bd9Sstevel@tonic-gate 	mutex_init(&nfscl->nfscl_chtable4_lock, NULL, MUTEX_DEFAULT, NULL);
26887c478bd9Sstevel@tonic-gate 	nfscl->nfscl_chtable4 = NULL;
26897c478bd9Sstevel@tonic-gate 	nfscl->nfscl_zoneid = zoneid;
26907c478bd9Sstevel@tonic-gate 
26917c478bd9Sstevel@tonic-gate 	bcopy(&clstat4_tmpl, &nfscl->nfscl_stat, sizeof (clstat4_tmpl));
26927c478bd9Sstevel@tonic-gate 	ndata = sizeof (clstat4_tmpl) / sizeof (kstat_named_t);
26937c478bd9Sstevel@tonic-gate #ifdef DEBUG
26947c478bd9Sstevel@tonic-gate 	ndata += sizeof (clstat4_debug) / sizeof (kstat_named_t);
26957c478bd9Sstevel@tonic-gate #endif
26967c478bd9Sstevel@tonic-gate 	if ((nfs4_client_kstat = kstat_create_zone("nfs", 0, "nfs4_client",
26977c478bd9Sstevel@tonic-gate 	    "misc", KSTAT_TYPE_NAMED, ndata,
26987c478bd9Sstevel@tonic-gate 	    KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE, zoneid)) != NULL) {
26997c478bd9Sstevel@tonic-gate 		nfs4_client_kstat->ks_private = &nfscl->nfscl_stat;
27007c478bd9Sstevel@tonic-gate 		nfs4_client_kstat->ks_snapshot = cl4_snapshot;
27017c478bd9Sstevel@tonic-gate 		kstat_install(nfs4_client_kstat);
27027c478bd9Sstevel@tonic-gate 	}
27037c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_clnt_list_lock);
27047c478bd9Sstevel@tonic-gate 	list_insert_head(&nfs4_clnt_list, nfscl);
27057c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_clnt_list_lock);
27067c478bd9Sstevel@tonic-gate 	return (nfscl);
27077c478bd9Sstevel@tonic-gate }
27087c478bd9Sstevel@tonic-gate 
27097c478bd9Sstevel@tonic-gate /*ARGSUSED*/
27107c478bd9Sstevel@tonic-gate static void
27117c478bd9Sstevel@tonic-gate clfini4_zone(zoneid_t zoneid, void *arg)
27127c478bd9Sstevel@tonic-gate {
27137c478bd9Sstevel@tonic-gate 	struct nfs4_clnt *nfscl = arg;
27147c478bd9Sstevel@tonic-gate 	chhead_t *chp, *next;
27157c478bd9Sstevel@tonic-gate 
27167c478bd9Sstevel@tonic-gate 	if (nfscl == NULL)
27177c478bd9Sstevel@tonic-gate 		return;
27187c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_clnt_list_lock);
27197c478bd9Sstevel@tonic-gate 	list_remove(&nfs4_clnt_list, nfscl);
27207c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_clnt_list_lock);
27217c478bd9Sstevel@tonic-gate 	clreclaim4_zone(nfscl, 0);
27227c478bd9Sstevel@tonic-gate 	for (chp = nfscl->nfscl_chtable4; chp != NULL; chp = next) {
27237c478bd9Sstevel@tonic-gate 		ASSERT(chp->ch_list == NULL);
27247c478bd9Sstevel@tonic-gate 		kmem_free(chp->ch_protofmly, strlen(chp->ch_protofmly) + 1);
27257c478bd9Sstevel@tonic-gate 		next = chp->ch_next;
27267c478bd9Sstevel@tonic-gate 		kmem_free(chp, sizeof (*chp));
27277c478bd9Sstevel@tonic-gate 	}
27287c478bd9Sstevel@tonic-gate 	kstat_delete_byname_zone("nfs", 0, "nfs4_client", zoneid);
27297c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfscl->nfscl_chtable4_lock);
27307c478bd9Sstevel@tonic-gate 	kmem_free(nfscl, sizeof (*nfscl));
27317c478bd9Sstevel@tonic-gate }
27327c478bd9Sstevel@tonic-gate 
27337c478bd9Sstevel@tonic-gate /*
27347c478bd9Sstevel@tonic-gate  * Called by endpnt_destructor to make sure the client handles are
27357c478bd9Sstevel@tonic-gate  * cleaned up before the RPC endpoints.  This becomes a no-op if
27367c478bd9Sstevel@tonic-gate  * clfini_zone (above) is called first.  This function is needed
27377c478bd9Sstevel@tonic-gate  * (rather than relying on clfini_zone to clean up) because the ZSD
27387c478bd9Sstevel@tonic-gate  * callbacks have no ordering mechanism, so we have no way to ensure
27397c478bd9Sstevel@tonic-gate  * that clfini_zone is called before endpnt_destructor.
27407c478bd9Sstevel@tonic-gate  */
27417c478bd9Sstevel@tonic-gate void
27427c478bd9Sstevel@tonic-gate clcleanup4_zone(zoneid_t zoneid)
27437c478bd9Sstevel@tonic-gate {
27447c478bd9Sstevel@tonic-gate 	struct nfs4_clnt *nfscl;
27457c478bd9Sstevel@tonic-gate 
27467c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_clnt_list_lock);
27477c478bd9Sstevel@tonic-gate 	nfscl = list_head(&nfs4_clnt_list);
27487c478bd9Sstevel@tonic-gate 	for (; nfscl != NULL; nfscl = list_next(&nfs4_clnt_list, nfscl)) {
27497c478bd9Sstevel@tonic-gate 		if (nfscl->nfscl_zoneid == zoneid) {
27507c478bd9Sstevel@tonic-gate 			clreclaim4_zone(nfscl, 0);
27517c478bd9Sstevel@tonic-gate 			break;
27527c478bd9Sstevel@tonic-gate 		}
27537c478bd9Sstevel@tonic-gate 	}
27547c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_clnt_list_lock);
27557c478bd9Sstevel@tonic-gate }
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate int
27587c478bd9Sstevel@tonic-gate nfs4_subr_init(void)
27597c478bd9Sstevel@tonic-gate {
27607c478bd9Sstevel@tonic-gate 	/*
27617c478bd9Sstevel@tonic-gate 	 * Allocate and initialize the client handle cache
27627c478bd9Sstevel@tonic-gate 	 */
27637c478bd9Sstevel@tonic-gate 	chtab4_cache = kmem_cache_create("client_handle4_cache",
27647c478bd9Sstevel@tonic-gate 		sizeof (struct chtab), 0, NULL, NULL, clreclaim4, NULL,
27657c478bd9Sstevel@tonic-gate 		NULL, 0);
27667c478bd9Sstevel@tonic-gate 
27677c478bd9Sstevel@tonic-gate 	/*
27687c478bd9Sstevel@tonic-gate 	 * Initialize the list of per-zone client handles (and associated data).
27697c478bd9Sstevel@tonic-gate 	 * This needs to be done before we call zone_key_create().
27707c478bd9Sstevel@tonic-gate 	 */
27717c478bd9Sstevel@tonic-gate 	list_create(&nfs4_clnt_list, sizeof (struct nfs4_clnt),
27727c478bd9Sstevel@tonic-gate 	    offsetof(struct nfs4_clnt, nfscl_node));
27737c478bd9Sstevel@tonic-gate 
27747c478bd9Sstevel@tonic-gate 	/*
27757c478bd9Sstevel@tonic-gate 	 * Initialize the zone_key for per-zone client handle lists.
27767c478bd9Sstevel@tonic-gate 	 */
27777c478bd9Sstevel@tonic-gate 	zone_key_create(&nfs4clnt_zone_key, clinit4_zone, NULL, clfini4_zone);
27787c478bd9Sstevel@tonic-gate 
27797c478bd9Sstevel@tonic-gate 	if (nfs4err_delay_time == 0)
27807c478bd9Sstevel@tonic-gate 		nfs4err_delay_time = NFS4ERR_DELAY_TIME;
27817c478bd9Sstevel@tonic-gate 
27827c478bd9Sstevel@tonic-gate 	return (0);
27837c478bd9Sstevel@tonic-gate }
27847c478bd9Sstevel@tonic-gate 
27857c478bd9Sstevel@tonic-gate int
27867c478bd9Sstevel@tonic-gate nfs4_subr_fini(void)
27877c478bd9Sstevel@tonic-gate {
27887c478bd9Sstevel@tonic-gate 	/*
27897c478bd9Sstevel@tonic-gate 	 * Deallocate the client handle cache
27907c478bd9Sstevel@tonic-gate 	 */
27917c478bd9Sstevel@tonic-gate 	kmem_cache_destroy(chtab4_cache);
27927c478bd9Sstevel@tonic-gate 
27937c478bd9Sstevel@tonic-gate 	/*
27947c478bd9Sstevel@tonic-gate 	 * Destroy the zone_key
27957c478bd9Sstevel@tonic-gate 	 */
27967c478bd9Sstevel@tonic-gate 	(void) zone_key_delete(nfs4clnt_zone_key);
27977c478bd9Sstevel@tonic-gate 
27987c478bd9Sstevel@tonic-gate 	return (0);
27997c478bd9Sstevel@tonic-gate }
28007c478bd9Sstevel@tonic-gate /*
28017c478bd9Sstevel@tonic-gate  * Set or Clear direct I/O flag
28027c478bd9Sstevel@tonic-gate  * VOP_RWLOCK() is held for write access to prevent a race condition
28037c478bd9Sstevel@tonic-gate  * which would occur if a process is in the middle of a write when
28047c478bd9Sstevel@tonic-gate  * directio flag gets set. It is possible that all pages may not get flushed.
28057c478bd9Sstevel@tonic-gate  *
28067c478bd9Sstevel@tonic-gate  * This is a copy of nfs_directio, changes here may need to be made
28077c478bd9Sstevel@tonic-gate  * there and vice versa.
28087c478bd9Sstevel@tonic-gate  */
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate int
28117c478bd9Sstevel@tonic-gate nfs4_directio(vnode_t *vp, int cmd, cred_t *cr)
28127c478bd9Sstevel@tonic-gate {
28137c478bd9Sstevel@tonic-gate 	int	error = 0;
28147c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
28157c478bd9Sstevel@tonic-gate 
28167c478bd9Sstevel@tonic-gate 	rp = VTOR4(vp);
28177c478bd9Sstevel@tonic-gate 
28187c478bd9Sstevel@tonic-gate 	if (cmd == DIRECTIO_ON) {
28197c478bd9Sstevel@tonic-gate 
28207c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R4DIRECTIO)
28217c478bd9Sstevel@tonic-gate 			return (0);
28227c478bd9Sstevel@tonic-gate 
28237c478bd9Sstevel@tonic-gate 		/*
28247c478bd9Sstevel@tonic-gate 		 * Flush the page cache.
28257c478bd9Sstevel@tonic-gate 		 */
28267c478bd9Sstevel@tonic-gate 
28277c478bd9Sstevel@tonic-gate 		(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
28287c478bd9Sstevel@tonic-gate 
28297c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R4DIRECTIO) {
28307c478bd9Sstevel@tonic-gate 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
28317c478bd9Sstevel@tonic-gate 			return (0);
28327c478bd9Sstevel@tonic-gate 		}
28337c478bd9Sstevel@tonic-gate 
28347c478bd9Sstevel@tonic-gate 		if (nfs4_has_pages(vp) &&
28357c478bd9Sstevel@tonic-gate 		    ((rp->r_flags & R4DIRTY) || rp->r_awcount > 0)) {
28367c478bd9Sstevel@tonic-gate 			error = VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0,
28377c478bd9Sstevel@tonic-gate 			    B_INVAL, cr);
28387c478bd9Sstevel@tonic-gate 			if (error) {
28397c478bd9Sstevel@tonic-gate 				if (error == ENOSPC || error == EDQUOT) {
28407c478bd9Sstevel@tonic-gate 					mutex_enter(&rp->r_statelock);
28417c478bd9Sstevel@tonic-gate 					if (!rp->r_error)
28427c478bd9Sstevel@tonic-gate 						rp->r_error = error;
28437c478bd9Sstevel@tonic-gate 					mutex_exit(&rp->r_statelock);
28447c478bd9Sstevel@tonic-gate 				}
28457c478bd9Sstevel@tonic-gate 				VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
28467c478bd9Sstevel@tonic-gate 				return (error);
28477c478bd9Sstevel@tonic-gate 			}
28487c478bd9Sstevel@tonic-gate 		}
28497c478bd9Sstevel@tonic-gate 
28507c478bd9Sstevel@tonic-gate 		mutex_enter(&rp->r_statelock);
28517c478bd9Sstevel@tonic-gate 		rp->r_flags |= R4DIRECTIO;
28527c478bd9Sstevel@tonic-gate 		mutex_exit(&rp->r_statelock);
28537c478bd9Sstevel@tonic-gate 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
28547c478bd9Sstevel@tonic-gate 		return (0);
28557c478bd9Sstevel@tonic-gate 	}
28567c478bd9Sstevel@tonic-gate 
28577c478bd9Sstevel@tonic-gate 	if (cmd == DIRECTIO_OFF) {
28587c478bd9Sstevel@tonic-gate 		mutex_enter(&rp->r_statelock);
28597c478bd9Sstevel@tonic-gate 		rp->r_flags &= ~R4DIRECTIO;	/* disable direct mode */
28607c478bd9Sstevel@tonic-gate 		mutex_exit(&rp->r_statelock);
28617c478bd9Sstevel@tonic-gate 		return (0);
28627c478bd9Sstevel@tonic-gate 	}
28637c478bd9Sstevel@tonic-gate 
28647c478bd9Sstevel@tonic-gate 	return (EINVAL);
28657c478bd9Sstevel@tonic-gate }
28667c478bd9Sstevel@tonic-gate 
28677c478bd9Sstevel@tonic-gate /*
28687c478bd9Sstevel@tonic-gate  * Return TRUE if the file has any pages.  Always go back to
28697c478bd9Sstevel@tonic-gate  * the master vnode to check v_pages since none of the shadows
28707c478bd9Sstevel@tonic-gate  * can have pages.
28717c478bd9Sstevel@tonic-gate  */
28727c478bd9Sstevel@tonic-gate 
28737c478bd9Sstevel@tonic-gate bool_t
28747c478bd9Sstevel@tonic-gate nfs4_has_pages(vnode_t *vp)
28757c478bd9Sstevel@tonic-gate {
28767c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
28777c478bd9Sstevel@tonic-gate 
28787c478bd9Sstevel@tonic-gate 	rp = VTOR4(vp);
28797c478bd9Sstevel@tonic-gate 	if (IS_SHADOW(vp, rp))
28807c478bd9Sstevel@tonic-gate 		vp = RTOV4(rp);	/* RTOV4 always gives the master */
28817c478bd9Sstevel@tonic-gate 
28827c478bd9Sstevel@tonic-gate 	return (vn_has_cached_data(vp));
28837c478bd9Sstevel@tonic-gate }
28847c478bd9Sstevel@tonic-gate 
28857c478bd9Sstevel@tonic-gate /*
28867c478bd9Sstevel@tonic-gate  * This table is used to determine whether the client should attempt
28877c478bd9Sstevel@tonic-gate  * failover based on the clnt_stat value returned by CLNT_CALL.  The
28887c478bd9Sstevel@tonic-gate  * clnt_stat is used as an index into the table.  If
28897c478bd9Sstevel@tonic-gate  * the error value that corresponds to the clnt_stat value in the
28907c478bd9Sstevel@tonic-gate  * table is non-zero, then that is the error to be returned AND
28917c478bd9Sstevel@tonic-gate  * that signals that failover should be attempted.
28927c478bd9Sstevel@tonic-gate  *
28937c478bd9Sstevel@tonic-gate  * Special note: If the RPC_ values change, then direct indexing of the
28947c478bd9Sstevel@tonic-gate  * table is no longer valid, but having the RPC_ values in the table
28957c478bd9Sstevel@tonic-gate  * allow the functions to detect the change and issue a warning.
28967c478bd9Sstevel@tonic-gate  * In this case, the code will always attempt failover as a defensive
28977c478bd9Sstevel@tonic-gate  * measure.
28987c478bd9Sstevel@tonic-gate  */
28997c478bd9Sstevel@tonic-gate 
29007c478bd9Sstevel@tonic-gate static struct try_failover_tab {
29017c478bd9Sstevel@tonic-gate 	enum clnt_stat	cstat;
29027c478bd9Sstevel@tonic-gate 	int		error;
29037c478bd9Sstevel@tonic-gate } try_failover_table [] = {
29047c478bd9Sstevel@tonic-gate 
29057c478bd9Sstevel@tonic-gate 	RPC_SUCCESS,		0,
29067c478bd9Sstevel@tonic-gate 	RPC_CANTENCODEARGS,	0,
29077c478bd9Sstevel@tonic-gate 	RPC_CANTDECODERES,	0,
29087c478bd9Sstevel@tonic-gate 	RPC_CANTSEND,		ECOMM,
29097c478bd9Sstevel@tonic-gate 	RPC_CANTRECV,		ECOMM,
29107c478bd9Sstevel@tonic-gate 	RPC_TIMEDOUT,		ETIMEDOUT,
29117c478bd9Sstevel@tonic-gate 	RPC_VERSMISMATCH,	0,
29127c478bd9Sstevel@tonic-gate 	RPC_AUTHERROR,		0,
29137c478bd9Sstevel@tonic-gate 	RPC_PROGUNAVAIL,	0,
29147c478bd9Sstevel@tonic-gate 	RPC_PROGVERSMISMATCH,	0,
29157c478bd9Sstevel@tonic-gate 	RPC_PROCUNAVAIL,	0,
29167c478bd9Sstevel@tonic-gate 	RPC_CANTDECODEARGS,	0,
29177c478bd9Sstevel@tonic-gate 	RPC_SYSTEMERROR,	ENOSR,
29187c478bd9Sstevel@tonic-gate 	RPC_UNKNOWNHOST,	EHOSTUNREACH,
29197c478bd9Sstevel@tonic-gate 	RPC_RPCBFAILURE,	ENETUNREACH,
29207c478bd9Sstevel@tonic-gate 	RPC_PROGNOTREGISTERED,	ECONNREFUSED,
29217c478bd9Sstevel@tonic-gate 	RPC_FAILED,		ETIMEDOUT,
29227c478bd9Sstevel@tonic-gate 	RPC_UNKNOWNPROTO,	EHOSTUNREACH,
29237c478bd9Sstevel@tonic-gate 	RPC_INTR,		0,
29247c478bd9Sstevel@tonic-gate 	RPC_UNKNOWNADDR,	EHOSTUNREACH,
29257c478bd9Sstevel@tonic-gate 	RPC_TLIERROR,		0,
29267c478bd9Sstevel@tonic-gate 	RPC_NOBROADCAST,	EHOSTUNREACH,
29277c478bd9Sstevel@tonic-gate 	RPC_N2AXLATEFAILURE,	ECONNREFUSED,
29287c478bd9Sstevel@tonic-gate 	RPC_UDERROR,		0,
29297c478bd9Sstevel@tonic-gate 	RPC_INPROGRESS,		0,
29307c478bd9Sstevel@tonic-gate 	RPC_STALERACHANDLE,	EINVAL,
29317c478bd9Sstevel@tonic-gate 	RPC_CANTCONNECT,	ECONNREFUSED,
29327c478bd9Sstevel@tonic-gate 	RPC_XPRTFAILED,		ECONNABORTED,
29337c478bd9Sstevel@tonic-gate 	RPC_CANTCREATESTREAM,	ECONNREFUSED,
29347c478bd9Sstevel@tonic-gate 	RPC_CANTSTORE,		ENOBUFS
29357c478bd9Sstevel@tonic-gate };
29367c478bd9Sstevel@tonic-gate 
29377c478bd9Sstevel@tonic-gate /*
29387c478bd9Sstevel@tonic-gate  * nfs4_try_failover - determine whether the client should
29397c478bd9Sstevel@tonic-gate  * attempt failover based on the values stored in the nfs4_error_t.
29407c478bd9Sstevel@tonic-gate  */
29417c478bd9Sstevel@tonic-gate int
29427c478bd9Sstevel@tonic-gate nfs4_try_failover(nfs4_error_t *ep)
29437c478bd9Sstevel@tonic-gate {
29447c478bd9Sstevel@tonic-gate 	if (ep->error == ETIMEDOUT || ep->stat == NFS4ERR_RESOURCE)
29457c478bd9Sstevel@tonic-gate 		return (TRUE);
29467c478bd9Sstevel@tonic-gate 
29477c478bd9Sstevel@tonic-gate 	if (ep->error && ep->rpc_status != RPC_SUCCESS)
29487c478bd9Sstevel@tonic-gate 		return (try_failover(ep->rpc_status) != 0 ? TRUE : FALSE);
29497c478bd9Sstevel@tonic-gate 
29507c478bd9Sstevel@tonic-gate 	return (FALSE);
29517c478bd9Sstevel@tonic-gate }
29527c478bd9Sstevel@tonic-gate 
29537c478bd9Sstevel@tonic-gate /*
29547c478bd9Sstevel@tonic-gate  * try_failover - internal version of nfs4_try_failover, called
29557c478bd9Sstevel@tonic-gate  * only by rfscall and aclcall.  Determine if failover is warranted
29567c478bd9Sstevel@tonic-gate  * based on the clnt_stat and return the error number if it is.
29577c478bd9Sstevel@tonic-gate  */
29587c478bd9Sstevel@tonic-gate static int
29597c478bd9Sstevel@tonic-gate try_failover(enum clnt_stat rpc_status)
29607c478bd9Sstevel@tonic-gate {
29617c478bd9Sstevel@tonic-gate 	int err = 0;
29627c478bd9Sstevel@tonic-gate 
29637c478bd9Sstevel@tonic-gate 	if (rpc_status == RPC_SUCCESS)
29647c478bd9Sstevel@tonic-gate 		return (0);
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate #ifdef	DEBUG
29677c478bd9Sstevel@tonic-gate 	if (rpc_status != 0 && nfs4_try_failover_any) {
29687c478bd9Sstevel@tonic-gate 		err = ETIMEDOUT;
29697c478bd9Sstevel@tonic-gate 		goto done;
29707c478bd9Sstevel@tonic-gate 	}
29717c478bd9Sstevel@tonic-gate #endif
29727c478bd9Sstevel@tonic-gate 	/*
29737c478bd9Sstevel@tonic-gate 	 * The rpc status is used as an index into the table.
29747c478bd9Sstevel@tonic-gate 	 * If the rpc status is outside of the range of the
29757c478bd9Sstevel@tonic-gate 	 * table or if the rpc error numbers have been changed
29767c478bd9Sstevel@tonic-gate 	 * since the table was constructed, then print a warning
29777c478bd9Sstevel@tonic-gate 	 * (DEBUG only) and try failover anyway.  Otherwise, just
29787c478bd9Sstevel@tonic-gate 	 * grab the resulting error number out of the table.
29797c478bd9Sstevel@tonic-gate 	 */
29807c478bd9Sstevel@tonic-gate 	if (rpc_status < RPC_SUCCESS || rpc_status >=
29817c478bd9Sstevel@tonic-gate 	    sizeof (try_failover_table)/sizeof (try_failover_table[0]) ||
29827c478bd9Sstevel@tonic-gate 	    try_failover_table[rpc_status].cstat != rpc_status) {
29837c478bd9Sstevel@tonic-gate 
29847c478bd9Sstevel@tonic-gate 		err = ETIMEDOUT;
29857c478bd9Sstevel@tonic-gate #ifdef	DEBUG
29867c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "try_failover: unexpected rpc error %d",
29877c478bd9Sstevel@tonic-gate 			rpc_status);
29887c478bd9Sstevel@tonic-gate #endif
29897c478bd9Sstevel@tonic-gate 	} else
29907c478bd9Sstevel@tonic-gate 		err = try_failover_table[rpc_status].error;
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate done:
29937c478bd9Sstevel@tonic-gate 	if (rpc_status)
29947c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
29957c478bd9Sstevel@tonic-gate 			"nfs4_try_failover: %strying failover on error %d",
29967c478bd9Sstevel@tonic-gate 			err ? "" : "NOT ", rpc_status));
29977c478bd9Sstevel@tonic-gate 
29987c478bd9Sstevel@tonic-gate 	return (err);
29997c478bd9Sstevel@tonic-gate }
30007c478bd9Sstevel@tonic-gate 
30017c478bd9Sstevel@tonic-gate void
30027c478bd9Sstevel@tonic-gate nfs4_error_zinit(nfs4_error_t *ep)
30037c478bd9Sstevel@tonic-gate {
30047c478bd9Sstevel@tonic-gate 	ep->error = 0;
30057c478bd9Sstevel@tonic-gate 	ep->stat = NFS4_OK;
30067c478bd9Sstevel@tonic-gate 	ep->rpc_status = RPC_SUCCESS;
30077c478bd9Sstevel@tonic-gate }
30087c478bd9Sstevel@tonic-gate 
30097c478bd9Sstevel@tonic-gate void
30107c478bd9Sstevel@tonic-gate nfs4_error_init(nfs4_error_t *ep, int error)
30117c478bd9Sstevel@tonic-gate {
30127c478bd9Sstevel@tonic-gate 	ep->error = error;
30137c478bd9Sstevel@tonic-gate 	ep->stat = NFS4_OK;
30147c478bd9Sstevel@tonic-gate 	ep->rpc_status = RPC_SUCCESS;
30157c478bd9Sstevel@tonic-gate }
30167c478bd9Sstevel@tonic-gate 
30177c478bd9Sstevel@tonic-gate 
30187c478bd9Sstevel@tonic-gate #ifdef DEBUG
30197c478bd9Sstevel@tonic-gate 
30207c478bd9Sstevel@tonic-gate /*
30217c478bd9Sstevel@tonic-gate  * Return a 16-bit hash for filehandle, stateid, clientid, owner.
30227c478bd9Sstevel@tonic-gate  * use the same algorithm as for NFS v3.
30237c478bd9Sstevel@tonic-gate  *
30247c478bd9Sstevel@tonic-gate  */
30257c478bd9Sstevel@tonic-gate int
30267c478bd9Sstevel@tonic-gate hash16(void *p, int len)
30277c478bd9Sstevel@tonic-gate {
30287c478bd9Sstevel@tonic-gate 	int i, rem;
30297c478bd9Sstevel@tonic-gate 	uint_t *wp;
30307c478bd9Sstevel@tonic-gate 	uint_t key = 0;
30317c478bd9Sstevel@tonic-gate 
30327c478bd9Sstevel@tonic-gate 	/* protect against non word aligned */
30337c478bd9Sstevel@tonic-gate 	if ((rem = len & 3) != 0)
30347c478bd9Sstevel@tonic-gate 		len &= ~3;
30357c478bd9Sstevel@tonic-gate 
30367c478bd9Sstevel@tonic-gate 	for (i = 0, wp = (uint_t *)p; i < len; i += 4, wp++) {
30377c478bd9Sstevel@tonic-gate 		key ^= (*wp >> 16) ^ *wp;
30387c478bd9Sstevel@tonic-gate 	}
30397c478bd9Sstevel@tonic-gate 
30407c478bd9Sstevel@tonic-gate 	/* hash left-over bytes */
30417c478bd9Sstevel@tonic-gate 	for (i = 0; i < rem; i++)
30427c478bd9Sstevel@tonic-gate 		key ^= *((uchar_t *)p + i);
30437c478bd9Sstevel@tonic-gate 
30447c478bd9Sstevel@tonic-gate 	return (key & 0xffff);
30457c478bd9Sstevel@tonic-gate }
30467c478bd9Sstevel@tonic-gate 
30477c478bd9Sstevel@tonic-gate /*
30487c478bd9Sstevel@tonic-gate  * rnode4info - return filehandle and path information for an rnode.
30497c478bd9Sstevel@tonic-gate  * XXX MT issues: uses a single static buffer, no locking of path.
30507c478bd9Sstevel@tonic-gate  */
30517c478bd9Sstevel@tonic-gate char *
30527c478bd9Sstevel@tonic-gate rnode4info(rnode4_t *rp)
30537c478bd9Sstevel@tonic-gate {
30547c478bd9Sstevel@tonic-gate 	static char buf[80];
30557c478bd9Sstevel@tonic-gate 	nfs4_fhandle_t fhandle;
30567c478bd9Sstevel@tonic-gate 	char *path;
30577c478bd9Sstevel@tonic-gate 	char *type;
30587c478bd9Sstevel@tonic-gate 
30597c478bd9Sstevel@tonic-gate 	if (rp == NULL)
30607c478bd9Sstevel@tonic-gate 		return ("null");
30617c478bd9Sstevel@tonic-gate 	if (rp->r_flags & R4ISXATTR)
30627c478bd9Sstevel@tonic-gate 		type = "attr";
30637c478bd9Sstevel@tonic-gate 	else if (RTOV4(rp)->v_flag & V_XATTRDIR)
30647c478bd9Sstevel@tonic-gate 		type = "attrdir";
30657c478bd9Sstevel@tonic-gate 	else if (RTOV4(rp)->v_flag & VROOT)
30667c478bd9Sstevel@tonic-gate 		type = "root";
30677c478bd9Sstevel@tonic-gate 	else if (RTOV4(rp)->v_type == VDIR)
30687c478bd9Sstevel@tonic-gate 		type = "dir";
30697c478bd9Sstevel@tonic-gate 	else if (RTOV4(rp)->v_type == VREG)
30707c478bd9Sstevel@tonic-gate 		type = "file";
30717c478bd9Sstevel@tonic-gate 	else
30727c478bd9Sstevel@tonic-gate 		type = "other";
30737c478bd9Sstevel@tonic-gate 	sfh4_copyval(rp->r_fh, &fhandle);
30747c478bd9Sstevel@tonic-gate 	path = fn_path(rp->r_svnode.sv_name);
30757c478bd9Sstevel@tonic-gate 	(void) snprintf(buf, 80, "$%p[%s], type=%s, flags=%04X, FH=%04X\n",
30767c478bd9Sstevel@tonic-gate 	    (void *)rp, path, type, rp->r_flags,
30777c478bd9Sstevel@tonic-gate 	    hash16((void *)&fhandle.fh_buf, fhandle.fh_len));
30787c478bd9Sstevel@tonic-gate 	kmem_free(path, strlen(path)+1);
30797c478bd9Sstevel@tonic-gate 	return (buf);
30807c478bd9Sstevel@tonic-gate }
30817c478bd9Sstevel@tonic-gate #endif
3082