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