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
5f86c6ccaSdm  * Common Development and Distribution License (the "License").
6f86c6ccaSdm  * 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 /*
22aa59c4cbSrsb  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  *	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
287c478bd9Sstevel@tonic-gate  *	All Rights Reserved
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <sys/param.h>
347c478bd9Sstevel@tonic-gate #include <sys/types.h>
357c478bd9Sstevel@tonic-gate #include <sys/systm.h>
367c478bd9Sstevel@tonic-gate #include <sys/cred.h>
377c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
38aa59c4cbSrsb #include <sys/vfs_opreg.h>
397c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
407c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
417c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
427c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
437c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
447c478bd9Sstevel@tonic-gate #include <sys/mount.h>
457c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
467c478bd9Sstevel@tonic-gate #include <sys/errno.h>
477c478bd9Sstevel@tonic-gate #include <sys/debug.h>
487c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
497c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
507c478bd9Sstevel@tonic-gate #include <sys/bootconf.h>
517c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
527c478bd9Sstevel@tonic-gate #include <sys/acl.h>
537c478bd9Sstevel@tonic-gate #include <sys/flock.h>
547c478bd9Sstevel@tonic-gate #include <sys/time.h>
557c478bd9Sstevel@tonic-gate #include <sys/disp.h>
567c478bd9Sstevel@tonic-gate #include <sys/policy.h>
577c478bd9Sstevel@tonic-gate #include <sys/socket.h>
587c478bd9Sstevel@tonic-gate #include <sys/netconfig.h>
597c478bd9Sstevel@tonic-gate #include <sys/dnlc.h>
607c478bd9Sstevel@tonic-gate #include <sys/list.h>
6145916cd2Sjpk #include <sys/mntent.h>
6245916cd2Sjpk #include <sys/tsol/label.h>
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate #include <rpc/types.h>
657c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
667c478bd9Sstevel@tonic-gate #include <rpc/rpcsec_gss.h>
677c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
707c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
717c478bd9Sstevel@tonic-gate #include <nfs/mount.h>
727c478bd9Sstevel@tonic-gate #include <nfs/nfs_acl.h>
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate #include <nfs/nfs4.h>
777c478bd9Sstevel@tonic-gate #include <nfs/rnode4.h>
787c478bd9Sstevel@tonic-gate #include <nfs/nfs4_clnt.h>
7939d3e169Sevanl #include <sys/fs/autofs.h>
8039d3e169Sevanl 
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate  * Arguments passed to thread to free data structures from forced unmount.
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate typedef struct {
87*b9238976Sth 	vfs_t	*fm_vfsp;
88*b9238976Sth 	int	fm_flag;
89*b9238976Sth 	cred_t	*fm_cr;
907c478bd9Sstevel@tonic-gate } freemountargs_t;
917c478bd9Sstevel@tonic-gate 
92*b9238976Sth static void	async_free_mount(vfs_t *, int, cred_t *);
93*b9238976Sth static void	nfs4_free_mount(vfs_t *, int, cred_t *);
947c478bd9Sstevel@tonic-gate static void	nfs4_free_mount_thread(freemountargs_t *);
957c478bd9Sstevel@tonic-gate static int nfs4_chkdup_servinfo4(servinfo4_t *, servinfo4_t *);
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate /*
987c478bd9Sstevel@tonic-gate  * From rpcsec module (common/rpcsec).
997c478bd9Sstevel@tonic-gate  */
1007c478bd9Sstevel@tonic-gate extern int sec_clnt_loadinfo(struct sec_data *, struct sec_data **, model_t);
1017c478bd9Sstevel@tonic-gate extern void sec_clnt_freeinfo(struct sec_data *);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate /*
1047c478bd9Sstevel@tonic-gate  * The order and contents of this structure must be kept in sync with that of
1057c478bd9Sstevel@tonic-gate  * rfsreqcnt_v4_tmpl in nfs_stats.c
1067c478bd9Sstevel@tonic-gate  */
1077c478bd9Sstevel@tonic-gate static char *rfsnames_v4[] = {
1087c478bd9Sstevel@tonic-gate 	"null", "compound", "reserved",	"access", "close", "commit", "create",
1097c478bd9Sstevel@tonic-gate 	"delegpurge", "delegreturn", "getattr",	"getfh", "link", "lock",
1107c478bd9Sstevel@tonic-gate 	"lockt", "locku", "lookup", "lookupp", "nverify", "open", "openattr",
1117c478bd9Sstevel@tonic-gate 	"open_confirm",	"open_downgrade", "putfh", "putpubfh", "putrootfh",
1127c478bd9Sstevel@tonic-gate 	"read", "readdir", "readlink", "remove", "rename", "renew",
1137c478bd9Sstevel@tonic-gate 	"restorefh", "savefh", "secinfo", "setattr", "setclientid",
1147c478bd9Sstevel@tonic-gate 	"setclientid_confirm", "verify", "write"
1157c478bd9Sstevel@tonic-gate };
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate /*
1187c478bd9Sstevel@tonic-gate  * nfs4_max_mount_retry is the number of times the client will redrive
1197c478bd9Sstevel@tonic-gate  * a mount compound before giving up and returning failure.  The intent
1207c478bd9Sstevel@tonic-gate  * is to redrive mount compounds which fail NFS4ERR_STALE so that
1217c478bd9Sstevel@tonic-gate  * if a component of the server path being mounted goes stale, it can
1227c478bd9Sstevel@tonic-gate  * "recover" by redriving the mount compund (LOOKUP ops).  This recovery
1237c478bd9Sstevel@tonic-gate  * code is needed outside of the recovery framework because mount is a
1247c478bd9Sstevel@tonic-gate  * special case.  The client doesn't create vnodes/rnodes for components
1257c478bd9Sstevel@tonic-gate  * of the server path being mounted.  The recovery code recovers real
1267c478bd9Sstevel@tonic-gate  * client objects, not STALE FHs which map to components of the server
1277c478bd9Sstevel@tonic-gate  * path being mounted.
1287c478bd9Sstevel@tonic-gate  *
1297c478bd9Sstevel@tonic-gate  * We could just fail the mount on the first time, but that would
1307c478bd9Sstevel@tonic-gate  * instantly trigger failover (from nfs4_mount), and the client should
1317c478bd9Sstevel@tonic-gate  * try to re-lookup the STALE FH before doing failover.  The easiest
1327c478bd9Sstevel@tonic-gate  * way to "re-lookup" is to simply redrive the mount compound.
1337c478bd9Sstevel@tonic-gate  */
1347c478bd9Sstevel@tonic-gate static int nfs4_max_mount_retry = 2;
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate /*
1377c478bd9Sstevel@tonic-gate  * nfs4 vfs operations.
1387c478bd9Sstevel@tonic-gate  */
139*b9238976Sth int		nfs4_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
1407c478bd9Sstevel@tonic-gate static int	nfs4_unmount(vfs_t *, int, cred_t *);
1417c478bd9Sstevel@tonic-gate static int	nfs4_root(vfs_t *, vnode_t **);
1427c478bd9Sstevel@tonic-gate static int	nfs4_statvfs(vfs_t *, struct statvfs64 *);
1437c478bd9Sstevel@tonic-gate static int	nfs4_sync(vfs_t *, short, cred_t *);
1447c478bd9Sstevel@tonic-gate static int	nfs4_vget(vfs_t *, vnode_t **, fid_t *);
1457c478bd9Sstevel@tonic-gate static int	nfs4_mountroot(vfs_t *, whymountroot_t);
1467c478bd9Sstevel@tonic-gate static void	nfs4_freevfs(vfs_t *);
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate static int	nfs4rootvp(vnode_t **, vfs_t *, struct servinfo4 *,
1497c478bd9Sstevel@tonic-gate 		    int, cred_t *, zone_t *);
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate vfsops_t	*nfs4_vfsops;
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate int nfs4_vfsinit(void);
1547c478bd9Sstevel@tonic-gate void nfs4_vfsfini(void);
1557c478bd9Sstevel@tonic-gate static void nfs4setclientid_init(void);
1567c478bd9Sstevel@tonic-gate static void nfs4setclientid_fini(void);
1577c478bd9Sstevel@tonic-gate static void nfs4setclientid_otw(mntinfo4_t *, servinfo4_t *,  cred_t *,
1587c478bd9Sstevel@tonic-gate 		struct nfs4_server *, nfs4_error_t *, int *);
1597c478bd9Sstevel@tonic-gate static void	destroy_nfs4_server(nfs4_server_t *);
1607c478bd9Sstevel@tonic-gate static void	remove_mi(nfs4_server_t *, mntinfo4_t *);
1617c478bd9Sstevel@tonic-gate 
162*b9238976Sth extern void nfs4_ephemeral_init(void);
163*b9238976Sth extern void nfs4_ephemeral_fini(void);
164*b9238976Sth 
1657c478bd9Sstevel@tonic-gate /*
1667c478bd9Sstevel@tonic-gate  * Initialize the vfs structure
1677c478bd9Sstevel@tonic-gate  */
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate static int nfs4fstyp;
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate /*
1737c478bd9Sstevel@tonic-gate  * Debug variable to check for rdma based
1747c478bd9Sstevel@tonic-gate  * transport startup and cleanup. Controlled
1757c478bd9Sstevel@tonic-gate  * through /etc/system. Off by default.
1767c478bd9Sstevel@tonic-gate  */
1777c478bd9Sstevel@tonic-gate extern int rdma_debug;
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate int
1807c478bd9Sstevel@tonic-gate nfs4init(int fstyp, char *name)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate 	static const fs_operation_def_t nfs4_vfsops_template[] = {
183aa59c4cbSrsb 		VFSNAME_MOUNT,		{ .vfs_mount = nfs4_mount },
184aa59c4cbSrsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = nfs4_unmount },
185aa59c4cbSrsb 		VFSNAME_ROOT,		{ .vfs_root = nfs4_root },
186aa59c4cbSrsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = nfs4_statvfs },
187aa59c4cbSrsb 		VFSNAME_SYNC,		{ .vfs_sync = nfs4_sync },
188aa59c4cbSrsb 		VFSNAME_VGET,		{ .vfs_vget = nfs4_vget },
189aa59c4cbSrsb 		VFSNAME_MOUNTROOT,	{ .vfs_mountroot = nfs4_mountroot },
190aa59c4cbSrsb 		VFSNAME_FREEVFS,	{ .vfs_freevfs = nfs4_freevfs },
191aa59c4cbSrsb 		NULL,			NULL
1927c478bd9Sstevel@tonic-gate 	};
1937c478bd9Sstevel@tonic-gate 	int error;
1947c478bd9Sstevel@tonic-gate 
195*b9238976Sth 	nfs4_vfsops = NULL;
196*b9238976Sth 	nfs4_vnodeops = NULL;
197*b9238976Sth 	nfs4_trigger_vnodeops = NULL;
198*b9238976Sth 
1997c478bd9Sstevel@tonic-gate 	error = vfs_setfsops(fstyp, nfs4_vfsops_template, &nfs4_vfsops);
2007c478bd9Sstevel@tonic-gate 	if (error != 0) {
2017c478bd9Sstevel@tonic-gate 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
2027c478bd9Sstevel@tonic-gate 		    "nfs4init: bad vfs ops template");
203*b9238976Sth 		goto out;
2047c478bd9Sstevel@tonic-gate 	}
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	error = vn_make_ops(name, nfs4_vnodeops_template, &nfs4_vnodeops);
2077c478bd9Sstevel@tonic-gate 	if (error != 0) {
2087c478bd9Sstevel@tonic-gate 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
2097c478bd9Sstevel@tonic-gate 		    "nfs4init: bad vnode ops template");
210*b9238976Sth 		goto out;
2117c478bd9Sstevel@tonic-gate 	}
2127c478bd9Sstevel@tonic-gate 
213*b9238976Sth 	error = vn_make_ops("nfs4_trigger", nfs4_trigger_vnodeops_template,
214*b9238976Sth 	    &nfs4_trigger_vnodeops);
215*b9238976Sth 	if (error != 0) {
216*b9238976Sth 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
217*b9238976Sth 		    "nfs4init: bad trigger vnode ops template");
218*b9238976Sth 		goto out;
219*b9238976Sth 	}
2207c478bd9Sstevel@tonic-gate 
221*b9238976Sth 	nfs4fstyp = fstyp;
2227c478bd9Sstevel@tonic-gate 	(void) nfs4_vfsinit();
2237c478bd9Sstevel@tonic-gate 	(void) nfs4_init_dot_entries();
2247c478bd9Sstevel@tonic-gate 
225*b9238976Sth out:
226*b9238976Sth 	if (error) {
227*b9238976Sth 		if (nfs4_trigger_vnodeops != NULL)
228*b9238976Sth 			vn_freevnodeops(nfs4_trigger_vnodeops);
229*b9238976Sth 
230*b9238976Sth 		if (nfs4_vnodeops != NULL)
231*b9238976Sth 			vn_freevnodeops(nfs4_vnodeops);
232*b9238976Sth 
233*b9238976Sth 		(void) vfs_freevfsops_by_type(fstyp);
234*b9238976Sth 	}
235*b9238976Sth 
236*b9238976Sth 	return (error);
2377c478bd9Sstevel@tonic-gate }
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate void
2407c478bd9Sstevel@tonic-gate nfs4fini(void)
2417c478bd9Sstevel@tonic-gate {
2427c478bd9Sstevel@tonic-gate 	(void) nfs4_destroy_dot_entries();
2437c478bd9Sstevel@tonic-gate 	nfs4_vfsfini();
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate /*
2477c478bd9Sstevel@tonic-gate  * Create a new sec_data structure to store AUTH_DH related data:
2487c478bd9Sstevel@tonic-gate  * netname, syncaddr, knetconfig. There is no AUTH_F_RPCTIMESYNC
2497c478bd9Sstevel@tonic-gate  * flag set for NFS V4 since we are avoiding to contact the rpcbind
2507c478bd9Sstevel@tonic-gate  * daemon and is using the IP time service (IPPORT_TIMESERVER).
2517c478bd9Sstevel@tonic-gate  *
2527c478bd9Sstevel@tonic-gate  * sec_data can be freed by sec_clnt_freeinfo().
2537c478bd9Sstevel@tonic-gate  */
254*b9238976Sth static struct sec_data *
2557c478bd9Sstevel@tonic-gate create_authdh_data(char *netname, int nlen, struct netbuf *syncaddr,
2567c478bd9Sstevel@tonic-gate 		struct knetconfig *knconf) {
2577c478bd9Sstevel@tonic-gate 	struct sec_data *secdata;
2587c478bd9Sstevel@tonic-gate 	dh_k4_clntdata_t *data;
2597c478bd9Sstevel@tonic-gate 	char *pf, *p;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	if (syncaddr == NULL || syncaddr->buf == NULL || nlen == 0)
2627c478bd9Sstevel@tonic-gate 		return (NULL);
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
2657c478bd9Sstevel@tonic-gate 	secdata->flags = 0;
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	data = kmem_alloc(sizeof (*data), KM_SLEEP);
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	data->syncaddr.maxlen = syncaddr->maxlen;
2707c478bd9Sstevel@tonic-gate 	data->syncaddr.len = syncaddr->len;
2717c478bd9Sstevel@tonic-gate 	data->syncaddr.buf = (char *)kmem_alloc(syncaddr->len, KM_SLEEP);
2727c478bd9Sstevel@tonic-gate 	bcopy(syncaddr->buf, data->syncaddr.buf, syncaddr->len);
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	/*
2757c478bd9Sstevel@tonic-gate 	 * duplicate the knconf information for the
2767c478bd9Sstevel@tonic-gate 	 * new opaque data.
2777c478bd9Sstevel@tonic-gate 	 */
2787c478bd9Sstevel@tonic-gate 	data->knconf = kmem_alloc(sizeof (*knconf), KM_SLEEP);
2797c478bd9Sstevel@tonic-gate 	*data->knconf = *knconf;
2807c478bd9Sstevel@tonic-gate 	pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
2817c478bd9Sstevel@tonic-gate 	p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
2827c478bd9Sstevel@tonic-gate 	bcopy(knconf->knc_protofmly, pf, KNC_STRSIZE);
2837c478bd9Sstevel@tonic-gate 	bcopy(knconf->knc_proto, p, KNC_STRSIZE);
2847c478bd9Sstevel@tonic-gate 	data->knconf->knc_protofmly = pf;
2857c478bd9Sstevel@tonic-gate 	data->knconf->knc_proto = p;
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	/* move server netname to the sec_data structure */
2887c478bd9Sstevel@tonic-gate 	data->netname = kmem_alloc(nlen, KM_SLEEP);
2897c478bd9Sstevel@tonic-gate 	bcopy(netname, data->netname, nlen);
2907c478bd9Sstevel@tonic-gate 	data->netnamelen = (int)nlen;
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	secdata->secmod = AUTH_DH;
2937c478bd9Sstevel@tonic-gate 	secdata->rpcflavor = AUTH_DH;
2947c478bd9Sstevel@tonic-gate 	secdata->data = (caddr_t)data;
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	return (secdata);
2977c478bd9Sstevel@tonic-gate }
2987c478bd9Sstevel@tonic-gate 
299*b9238976Sth /*
300*b9238976Sth  * Returns (deep) copy of sec_data_t. Allocates all memory required; caller
301*b9238976Sth  * is responsible for freeing.
302*b9238976Sth  */
303*b9238976Sth sec_data_t *
304*b9238976Sth copy_sec_data(sec_data_t *fsecdata) {
305*b9238976Sth 	sec_data_t *tsecdata;
306*b9238976Sth 
307*b9238976Sth 	if (fsecdata == NULL)
308*b9238976Sth 		return (NULL);
309*b9238976Sth 
310*b9238976Sth 	if (fsecdata->rpcflavor == AUTH_DH) {
311*b9238976Sth 		dh_k4_clntdata_t *fdata = (dh_k4_clntdata_t *)fsecdata->data;
312*b9238976Sth 
313*b9238976Sth 		if (fdata == NULL)
314*b9238976Sth 			return (NULL);
315*b9238976Sth 
316*b9238976Sth 		tsecdata = (sec_data_t *)create_authdh_data(fdata->netname,
317*b9238976Sth 		    fdata->netnamelen, &fdata->syncaddr, fdata->knconf);
318*b9238976Sth 
319*b9238976Sth 		return (tsecdata);
320*b9238976Sth 	}
321*b9238976Sth 
322*b9238976Sth 	tsecdata = kmem_zalloc(sizeof (sec_data_t), KM_SLEEP);
323*b9238976Sth 
324*b9238976Sth 	tsecdata->secmod = fsecdata->secmod;
325*b9238976Sth 	tsecdata->rpcflavor = fsecdata->rpcflavor;
326*b9238976Sth 	tsecdata->flags = fsecdata->flags;
327*b9238976Sth 	tsecdata->uid = fsecdata->uid;
328*b9238976Sth 
329*b9238976Sth 	if (fsecdata->rpcflavor == RPCSEC_GSS) {
330*b9238976Sth 		gss_clntdata_t *gcd = (gss_clntdata_t *)fsecdata->data;
331*b9238976Sth 
332*b9238976Sth 		tsecdata->data = (caddr_t)copy_sec_data_gss(gcd);
333*b9238976Sth 	} else {
334*b9238976Sth 		tsecdata->data = NULL;
335*b9238976Sth 	}
336*b9238976Sth 
337*b9238976Sth 	return (tsecdata);
338*b9238976Sth }
339*b9238976Sth 
340*b9238976Sth gss_clntdata_t *
341*b9238976Sth copy_sec_data_gss(gss_clntdata_t *fdata)
342*b9238976Sth {
343*b9238976Sth 	gss_clntdata_t *tdata;
344*b9238976Sth 
345*b9238976Sth 	if (fdata == NULL)
346*b9238976Sth 		return (NULL);
347*b9238976Sth 
348*b9238976Sth 	tdata = kmem_zalloc(sizeof (gss_clntdata_t), KM_SLEEP);
349*b9238976Sth 
350*b9238976Sth 	tdata->mechanism.length = fdata->mechanism.length;
351*b9238976Sth 	tdata->mechanism.elements = kmem_zalloc(fdata->mechanism.length,
352*b9238976Sth 	    KM_SLEEP);
353*b9238976Sth 	bcopy(fdata->mechanism.elements, tdata->mechanism.elements,
354*b9238976Sth 	    fdata->mechanism.length);
355*b9238976Sth 
356*b9238976Sth 	tdata->service = fdata->service;
357*b9238976Sth 
358*b9238976Sth 	(void) strcpy(tdata->uname, fdata->uname);
359*b9238976Sth 	(void) strcpy(tdata->inst, fdata->inst);
360*b9238976Sth 	(void) strcpy(tdata->realm, fdata->realm);
361*b9238976Sth 
362*b9238976Sth 	tdata->qop = fdata->qop;
363*b9238976Sth 
364*b9238976Sth 	return (tdata);
365*b9238976Sth }
366*b9238976Sth 
3677c478bd9Sstevel@tonic-gate static int
3687c478bd9Sstevel@tonic-gate nfs4_chkdup_servinfo4(servinfo4_t *svp_head, servinfo4_t *svp)
3697c478bd9Sstevel@tonic-gate {
3707c478bd9Sstevel@tonic-gate 	servinfo4_t *si;
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	/*
3737c478bd9Sstevel@tonic-gate 	 * Iterate over the servinfo4 list to make sure
3747c478bd9Sstevel@tonic-gate 	 * we do not have a duplicate. Skip any servinfo4
3757c478bd9Sstevel@tonic-gate 	 * that has been marked "NOT IN USE"
3767c478bd9Sstevel@tonic-gate 	 */
3777c478bd9Sstevel@tonic-gate 	for (si = svp_head; si; si = si->sv_next) {
3787c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&si->sv_lock, RW_READER, 0);
3797c478bd9Sstevel@tonic-gate 		if (si->sv_flags & SV4_NOTINUSE) {
3807c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&si->sv_lock);
3817c478bd9Sstevel@tonic-gate 			continue;
3827c478bd9Sstevel@tonic-gate 		}
3837c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&si->sv_lock);
3847c478bd9Sstevel@tonic-gate 		if (si == svp)
3857c478bd9Sstevel@tonic-gate 			continue;
3867c478bd9Sstevel@tonic-gate 		if (si->sv_addr.len == svp->sv_addr.len &&
3877c478bd9Sstevel@tonic-gate 		    strcmp(si->sv_knconf->knc_protofmly,
388*b9238976Sth 		    svp->sv_knconf->knc_protofmly) == 0 &&
3897c478bd9Sstevel@tonic-gate 		    bcmp(si->sv_addr.buf, svp->sv_addr.buf,
390*b9238976Sth 		    si->sv_addr.len) == 0) {
3917c478bd9Sstevel@tonic-gate 			/* it's a duplicate */
3927c478bd9Sstevel@tonic-gate 			return (1);
3937c478bd9Sstevel@tonic-gate 		}
3947c478bd9Sstevel@tonic-gate 	}
3957c478bd9Sstevel@tonic-gate 	/* it's not a duplicate */
3967c478bd9Sstevel@tonic-gate 	return (0);
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate 
39939d3e169Sevanl void
40039d3e169Sevanl nfs4_free_args(struct nfs_args *nargs)
40139d3e169Sevanl {
40239d3e169Sevanl 	if (nargs->knconf) {
40339d3e169Sevanl 		if (nargs->knconf->knc_protofmly)
40439d3e169Sevanl 			kmem_free(nargs->knconf->knc_protofmly,
405*b9238976Sth 			    KNC_STRSIZE);
40639d3e169Sevanl 		if (nargs->knconf->knc_proto)
40739d3e169Sevanl 			kmem_free(nargs->knconf->knc_proto, KNC_STRSIZE);
40839d3e169Sevanl 		kmem_free(nargs->knconf, sizeof (*nargs->knconf));
40939d3e169Sevanl 		nargs->knconf = NULL;
41039d3e169Sevanl 	}
41139d3e169Sevanl 
41239d3e169Sevanl 	if (nargs->fh) {
41339d3e169Sevanl 		kmem_free(nargs->fh, strlen(nargs->fh) + 1);
41439d3e169Sevanl 		nargs->fh = NULL;
41539d3e169Sevanl 	}
41639d3e169Sevanl 
41739d3e169Sevanl 	if (nargs->hostname) {
41839d3e169Sevanl 		kmem_free(nargs->hostname, strlen(nargs->hostname) + 1);
41939d3e169Sevanl 		nargs->hostname = NULL;
42039d3e169Sevanl 	}
42139d3e169Sevanl 
42239d3e169Sevanl 	if (nargs->addr) {
42339d3e169Sevanl 		if (nargs->addr->buf) {
42439d3e169Sevanl 			ASSERT(nargs->addr->len);
42539d3e169Sevanl 			kmem_free(nargs->addr->buf, nargs->addr->len);
42639d3e169Sevanl 		}
42739d3e169Sevanl 		kmem_free(nargs->addr, sizeof (struct netbuf));
42839d3e169Sevanl 		nargs->addr = NULL;
42939d3e169Sevanl 	}
43039d3e169Sevanl 
43139d3e169Sevanl 	if (nargs->syncaddr) {
43239d3e169Sevanl 		ASSERT(nargs->syncaddr->len);
43339d3e169Sevanl 		if (nargs->syncaddr->buf) {
43439d3e169Sevanl 			ASSERT(nargs->syncaddr->len);
43539d3e169Sevanl 			kmem_free(nargs->syncaddr->buf, nargs->syncaddr->len);
43639d3e169Sevanl 		}
43739d3e169Sevanl 		kmem_free(nargs->syncaddr, sizeof (struct netbuf));
43839d3e169Sevanl 		nargs->syncaddr = NULL;
43939d3e169Sevanl 	}
44039d3e169Sevanl 
44139d3e169Sevanl 	if (nargs->netname) {
44239d3e169Sevanl 		kmem_free(nargs->netname, strlen(nargs->netname) + 1);
44339d3e169Sevanl 		nargs->netname = NULL;
44439d3e169Sevanl 	}
44539d3e169Sevanl 
44639d3e169Sevanl 	if (nargs->nfs_ext_u.nfs_extA.secdata) {
44739d3e169Sevanl 		sec_clnt_freeinfo(
448*b9238976Sth 		    nargs->nfs_ext_u.nfs_extA.secdata);
44939d3e169Sevanl 		nargs->nfs_ext_u.nfs_extA.secdata = NULL;
45039d3e169Sevanl 	}
45139d3e169Sevanl }
45239d3e169Sevanl 
45339d3e169Sevanl 
45439d3e169Sevanl int
45539d3e169Sevanl nfs4_copyin(char *data, int datalen, struct nfs_args *nargs)
45639d3e169Sevanl {
45739d3e169Sevanl 
45839d3e169Sevanl 	int error;
45939d3e169Sevanl 	size_t hlen;			/* length of hostname */
46039d3e169Sevanl 	size_t nlen;			/* length of netname */
46139d3e169Sevanl 	char netname[MAXNETNAMELEN+1];	/* server's netname */
46239d3e169Sevanl 	struct netbuf addr;		/* server's address */
46339d3e169Sevanl 	struct netbuf syncaddr;		/* AUTH_DES time sync addr */
46439d3e169Sevanl 	struct knetconfig *knconf;		/* transport structure */
46539d3e169Sevanl 	struct sec_data *secdata = NULL;	/* security data */
46639d3e169Sevanl 	STRUCT_DECL(nfs_args, args);		/* nfs mount arguments */
46739d3e169Sevanl 	STRUCT_DECL(knetconfig, knconf_tmp);
46839d3e169Sevanl 	STRUCT_DECL(netbuf, addr_tmp);
46939d3e169Sevanl 	int flags;
47039d3e169Sevanl 	char *p, *pf;
47139d3e169Sevanl 	struct pathname pn;
47239d3e169Sevanl 	char *userbufptr;
47339d3e169Sevanl 
47439d3e169Sevanl 
47539d3e169Sevanl 	bzero(nargs, sizeof (*nargs));
47639d3e169Sevanl 
47739d3e169Sevanl 	STRUCT_INIT(args, get_udatamodel());
47839d3e169Sevanl 	bzero(STRUCT_BUF(args), SIZEOF_STRUCT(nfs_args, DATAMODEL_NATIVE));
47939d3e169Sevanl 	if (copyin(data, STRUCT_BUF(args), MIN(datalen,
48039d3e169Sevanl 	    STRUCT_SIZE(args))))
48139d3e169Sevanl 		return (EFAULT);
48239d3e169Sevanl 
48339d3e169Sevanl 	nargs->wsize = STRUCT_FGET(args, wsize);
48439d3e169Sevanl 	nargs->rsize = STRUCT_FGET(args, rsize);
48539d3e169Sevanl 	nargs->timeo = STRUCT_FGET(args, timeo);
48639d3e169Sevanl 	nargs->retrans = STRUCT_FGET(args, retrans);
48739d3e169Sevanl 	nargs->acregmin = STRUCT_FGET(args, acregmin);
48839d3e169Sevanl 	nargs->acregmax = STRUCT_FGET(args, acregmax);
48939d3e169Sevanl 	nargs->acdirmin = STRUCT_FGET(args, acdirmin);
49039d3e169Sevanl 	nargs->acdirmax = STRUCT_FGET(args, acdirmax);
49139d3e169Sevanl 
49239d3e169Sevanl 	flags = STRUCT_FGET(args, flags);
49339d3e169Sevanl 	nargs->flags = flags;
49439d3e169Sevanl 
49539d3e169Sevanl 	addr.buf = NULL;
49639d3e169Sevanl 	syncaddr.buf = NULL;
49739d3e169Sevanl 
49839d3e169Sevanl 
49939d3e169Sevanl 	/*
50039d3e169Sevanl 	 * Allocate space for a knetconfig structure and
50139d3e169Sevanl 	 * its strings and copy in from user-land.
50239d3e169Sevanl 	 */
50339d3e169Sevanl 	knconf = kmem_zalloc(sizeof (*knconf), KM_SLEEP);
50439d3e169Sevanl 	STRUCT_INIT(knconf_tmp, get_udatamodel());
50539d3e169Sevanl 	if (copyin(STRUCT_FGETP(args, knconf), STRUCT_BUF(knconf_tmp),
50639d3e169Sevanl 	    STRUCT_SIZE(knconf_tmp))) {
50739d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
50839d3e169Sevanl 		return (EFAULT);
50939d3e169Sevanl 	}
51039d3e169Sevanl 
51139d3e169Sevanl 	knconf->knc_semantics = STRUCT_FGET(knconf_tmp, knc_semantics);
51239d3e169Sevanl 	knconf->knc_protofmly = STRUCT_FGETP(knconf_tmp, knc_protofmly);
51339d3e169Sevanl 	knconf->knc_proto = STRUCT_FGETP(knconf_tmp, knc_proto);
51439d3e169Sevanl 	if (get_udatamodel() != DATAMODEL_LP64) {
51539d3e169Sevanl 		knconf->knc_rdev = expldev(STRUCT_FGET(knconf_tmp, knc_rdev));
51639d3e169Sevanl 	} else {
51739d3e169Sevanl 		knconf->knc_rdev = STRUCT_FGET(knconf_tmp, knc_rdev);
51839d3e169Sevanl 	}
51939d3e169Sevanl 
52039d3e169Sevanl 	pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
52139d3e169Sevanl 	p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
52239d3e169Sevanl 	error = copyinstr(knconf->knc_protofmly, pf, KNC_STRSIZE, NULL);
52339d3e169Sevanl 	if (error) {
52439d3e169Sevanl 		kmem_free(pf, KNC_STRSIZE);
52539d3e169Sevanl 		kmem_free(p, KNC_STRSIZE);
52639d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
52739d3e169Sevanl 		return (error);
52839d3e169Sevanl 	}
52939d3e169Sevanl 
53039d3e169Sevanl 	error = copyinstr(knconf->knc_proto, p, KNC_STRSIZE, NULL);
53139d3e169Sevanl 	if (error) {
53239d3e169Sevanl 		kmem_free(pf, KNC_STRSIZE);
53339d3e169Sevanl 		kmem_free(p, KNC_STRSIZE);
53439d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
53539d3e169Sevanl 		return (error);
53639d3e169Sevanl 	}
53739d3e169Sevanl 
53839d3e169Sevanl 
53939d3e169Sevanl 	knconf->knc_protofmly = pf;
54039d3e169Sevanl 	knconf->knc_proto = p;
54139d3e169Sevanl 
54239d3e169Sevanl 	nargs->knconf = knconf;
54339d3e169Sevanl 
54439d3e169Sevanl 	/*
54539d3e169Sevanl 	 * Get server address
54639d3e169Sevanl 	 */
54739d3e169Sevanl 	STRUCT_INIT(addr_tmp, get_udatamodel());
54839d3e169Sevanl 	if (copyin(STRUCT_FGETP(args, addr), STRUCT_BUF(addr_tmp),
54939d3e169Sevanl 	    STRUCT_SIZE(addr_tmp))) {
55039d3e169Sevanl 		error = EFAULT;
55139d3e169Sevanl 		goto errout;
55239d3e169Sevanl 	}
55339d3e169Sevanl 
55439d3e169Sevanl 	nargs->addr = kmem_alloc(sizeof (struct netbuf), KM_SLEEP);
55539d3e169Sevanl 	userbufptr = STRUCT_FGETP(addr_tmp, buf);
55639d3e169Sevanl 	addr.len = STRUCT_FGET(addr_tmp, len);
55739d3e169Sevanl 	addr.buf = kmem_alloc(addr.len, KM_SLEEP);
55839d3e169Sevanl 	addr.maxlen = addr.len;
55939d3e169Sevanl 	if (copyin(userbufptr, addr.buf, addr.len)) {
56039d3e169Sevanl 		kmem_free(addr.buf, addr.len);
56139d3e169Sevanl 		error = EFAULT;
56239d3e169Sevanl 		goto errout;
56339d3e169Sevanl 	}
56439d3e169Sevanl 	bcopy(&addr, nargs->addr, sizeof (struct netbuf));
56539d3e169Sevanl 
56639d3e169Sevanl 	/*
56739d3e169Sevanl 	 * Get the root fhandle
56839d3e169Sevanl 	 */
56939d3e169Sevanl 	error = pn_get(STRUCT_FGETP(args, fh), UIO_USERSPACE, &pn);
57039d3e169Sevanl 	if (error)
57139d3e169Sevanl 		goto errout;
57239d3e169Sevanl 
57339d3e169Sevanl 	/* Volatile fh: keep server paths, so use actual-size strings */
57439d3e169Sevanl 	nargs->fh = kmem_alloc(pn.pn_pathlen + 1, KM_SLEEP);
57539d3e169Sevanl 	bcopy(pn.pn_path, nargs->fh, pn.pn_pathlen);
57639d3e169Sevanl 	nargs->fh[pn.pn_pathlen] = '\0';
57739d3e169Sevanl 	pn_free(&pn);
57839d3e169Sevanl 
57939d3e169Sevanl 
58039d3e169Sevanl 	/*
58139d3e169Sevanl 	 * Get server's hostname
58239d3e169Sevanl 	 */
58339d3e169Sevanl 	if (flags & NFSMNT_HOSTNAME) {
58439d3e169Sevanl 		error = copyinstr(STRUCT_FGETP(args, hostname),
585*b9238976Sth 		    netname, sizeof (netname), &hlen);
58639d3e169Sevanl 		if (error)
58739d3e169Sevanl 			goto errout;
58839d3e169Sevanl 		nargs->hostname = kmem_zalloc(hlen, KM_SLEEP);
58939d3e169Sevanl 		(void) strcpy(nargs->hostname, netname);
59039d3e169Sevanl 
59139d3e169Sevanl 	} else {
59239d3e169Sevanl 		nargs->hostname = NULL;
59339d3e169Sevanl 	}
59439d3e169Sevanl 
59539d3e169Sevanl 
59639d3e169Sevanl 	/*
59739d3e169Sevanl 	 * If there are syncaddr and netname data, load them in. This is
59839d3e169Sevanl 	 * to support data needed for NFSV4 when AUTH_DH is the negotiated
59939d3e169Sevanl 	 * flavor via SECINFO. (instead of using MOUNT protocol in V3).
60039d3e169Sevanl 	 */
60139d3e169Sevanl 	netname[0] = '\0';
60239d3e169Sevanl 	if (flags & NFSMNT_SECURE) {
60339d3e169Sevanl 
60439d3e169Sevanl 		/* get syncaddr */
60539d3e169Sevanl 		STRUCT_INIT(addr_tmp, get_udatamodel());
60639d3e169Sevanl 		if (copyin(STRUCT_FGETP(args, syncaddr), STRUCT_BUF(addr_tmp),
607*b9238976Sth 		    STRUCT_SIZE(addr_tmp))) {
60839d3e169Sevanl 			error = EINVAL;
60939d3e169Sevanl 			goto errout;
61039d3e169Sevanl 		}
61139d3e169Sevanl 		userbufptr = STRUCT_FGETP(addr_tmp, buf);
61239d3e169Sevanl 		syncaddr.len = STRUCT_FGET(addr_tmp, len);
61339d3e169Sevanl 		syncaddr.buf = kmem_alloc(syncaddr.len, KM_SLEEP);
61439d3e169Sevanl 		syncaddr.maxlen = syncaddr.len;
61539d3e169Sevanl 		if (copyin(userbufptr, syncaddr.buf, syncaddr.len)) {
61639d3e169Sevanl 			kmem_free(syncaddr.buf, syncaddr.len);
61739d3e169Sevanl 			error = EFAULT;
61839d3e169Sevanl 			goto errout;
61939d3e169Sevanl 		}
62039d3e169Sevanl 
62139d3e169Sevanl 		nargs->syncaddr = kmem_alloc(sizeof (struct netbuf), KM_SLEEP);
62239d3e169Sevanl 		bcopy(&syncaddr, nargs->syncaddr, sizeof (struct netbuf));
62339d3e169Sevanl 
62422d5e933Skr 		/* get server's netname */
62522d5e933Skr 		if (copyinstr(STRUCT_FGETP(args, netname), netname,
626*b9238976Sth 		    sizeof (netname), &nlen)) {
62722d5e933Skr 			error = EFAULT;
62822d5e933Skr 			goto errout;
62922d5e933Skr 		}
63022d5e933Skr 
63122d5e933Skr 		netname[nlen] = '\0';
63222d5e933Skr 		nargs->netname = kmem_zalloc(nlen, KM_SLEEP);
63322d5e933Skr 		(void) strcpy(nargs->netname, netname);
63422d5e933Skr 	}
63539d3e169Sevanl 
63639d3e169Sevanl 	/*
63739d3e169Sevanl 	 * Get the extention data which has the security data structure.
63839d3e169Sevanl 	 * This includes data for AUTH_SYS as well.
63939d3e169Sevanl 	 */
64039d3e169Sevanl 	if (flags & NFSMNT_NEWARGS) {
64139d3e169Sevanl 		nargs->nfs_args_ext = STRUCT_FGET(args, nfs_args_ext);
64239d3e169Sevanl 		if (nargs->nfs_args_ext == NFS_ARGS_EXTA ||
643*b9238976Sth 		    nargs->nfs_args_ext == NFS_ARGS_EXTB) {
64439d3e169Sevanl 			/*
64539d3e169Sevanl 			 * Indicating the application is using the new
64639d3e169Sevanl 			 * sec_data structure to pass in the security
64739d3e169Sevanl 			 * data.
64839d3e169Sevanl 			 */
64939d3e169Sevanl 			if (STRUCT_FGETP(args,
65039d3e169Sevanl 			    nfs_ext_u.nfs_extA.secdata) != NULL) {
65139d3e169Sevanl 				error = sec_clnt_loadinfo(
65239d3e169Sevanl 				    (struct sec_data *)STRUCT_FGETP(args,
653*b9238976Sth 				    nfs_ext_u.nfs_extA.secdata),
65439d3e169Sevanl 				    &secdata, get_udatamodel());
65539d3e169Sevanl 			}
65639d3e169Sevanl 			nargs->nfs_ext_u.nfs_extA.secdata = secdata;
65739d3e169Sevanl 		}
65839d3e169Sevanl 	}
65939d3e169Sevanl 
66039d3e169Sevanl 	if (error)
66139d3e169Sevanl 		goto errout;
66239d3e169Sevanl 
66339d3e169Sevanl 	/*
66439d3e169Sevanl 	 * Failover support:
66539d3e169Sevanl 	 *
66639d3e169Sevanl 	 * We may have a linked list of nfs_args structures,
66739d3e169Sevanl 	 * which means the user is looking for failover.  If
66839d3e169Sevanl 	 * the mount is either not "read-only" or "soft",
66939d3e169Sevanl 	 * we want to bail out with EINVAL.
67039d3e169Sevanl 	 */
67139d3e169Sevanl 	if (nargs->nfs_args_ext == NFS_ARGS_EXTB)
67239d3e169Sevanl 		nargs->nfs_ext_u.nfs_extB.next =
673*b9238976Sth 		    STRUCT_FGETP(args, nfs_ext_u.nfs_extB.next);
67439d3e169Sevanl 
67539d3e169Sevanl errout:
67639d3e169Sevanl 	if (error)
67739d3e169Sevanl 		nfs4_free_args(nargs);
67839d3e169Sevanl 
67939d3e169Sevanl 	return (error);
68039d3e169Sevanl }
68139d3e169Sevanl 
68239d3e169Sevanl 
6837c478bd9Sstevel@tonic-gate /*
6847c478bd9Sstevel@tonic-gate  * nfs mount vfsop
6857c478bd9Sstevel@tonic-gate  * Set up mount info record and attach it to vfs struct.
6867c478bd9Sstevel@tonic-gate  */
687*b9238976Sth int
6887c478bd9Sstevel@tonic-gate nfs4_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
6897c478bd9Sstevel@tonic-gate {
6907c478bd9Sstevel@tonic-gate 	char *data = uap->dataptr;
6917c478bd9Sstevel@tonic-gate 	int error;
6927c478bd9Sstevel@tonic-gate 	vnode_t *rtvp;			/* the server's root */
6937c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;			/* mount info, pointed at by vfs */
6947c478bd9Sstevel@tonic-gate 	struct knetconfig *rdma_knconf;	/* rdma transport structure */
6957c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
6967c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp;		/* nfs server info */
6977c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_tail = NULL; /* previous nfs server info */
6987c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_head;	/* first nfs server info */
6997c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_2ndlast;	/* 2nd last in server info list */
7007c478bd9Sstevel@tonic-gate 	struct sec_data *secdata;	/* security data */
70139d3e169Sevanl 	struct nfs_args *args = NULL;
70250a83466Sjwahlig 	int flags, addr_type, removed;
703108322fbScarlsonj 	zone_t *zone = nfs_zone();
7047c478bd9Sstevel@tonic-gate 	nfs4_error_t n4e;
70545916cd2Sjpk 	zone_t *mntzone = NULL;
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
7087c478bd9Sstevel@tonic-gate 		return (EPERM);
7097c478bd9Sstevel@tonic-gate 	if (mvp->v_type != VDIR)
7107c478bd9Sstevel@tonic-gate 		return (ENOTDIR);
711*b9238976Sth 
7127c478bd9Sstevel@tonic-gate 	/*
7137c478bd9Sstevel@tonic-gate 	 * get arguments
7147c478bd9Sstevel@tonic-gate 	 *
7157c478bd9Sstevel@tonic-gate 	 * nfs_args is now versioned and is extensible, so
7167c478bd9Sstevel@tonic-gate 	 * uap->datalen might be different from sizeof (args)
7177c478bd9Sstevel@tonic-gate 	 * in a compatible situation.
7187c478bd9Sstevel@tonic-gate 	 */
7197c478bd9Sstevel@tonic-gate more:
72039d3e169Sevanl 	if (!(uap->flags & MS_SYSSPACE)) {
72139d3e169Sevanl 		if (args == NULL)
72239d3e169Sevanl 			args = kmem_zalloc(sizeof (struct nfs_args), KM_SLEEP);
72339d3e169Sevanl 		else
72439d3e169Sevanl 			nfs4_free_args(args);
72539d3e169Sevanl 		error = nfs4_copyin(data, uap->datalen, args);
72639d3e169Sevanl 		if (error) {
72739d3e169Sevanl 			if (args) {
72839d3e169Sevanl 				kmem_free(args, sizeof (*args));
72939d3e169Sevanl 			}
73039d3e169Sevanl 			return (error);
73139d3e169Sevanl 		}
73239d3e169Sevanl 	} else {
73339d3e169Sevanl 		args = (struct nfs_args *)data;
73439d3e169Sevanl 	}
7357c478bd9Sstevel@tonic-gate 
73639d3e169Sevanl 	flags = args->flags;
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 	/*
7397c478bd9Sstevel@tonic-gate 	 * If the request changes the locking type, disallow the remount,
7407c478bd9Sstevel@tonic-gate 	 * because it's questionable whether we can transfer the
7417c478bd9Sstevel@tonic-gate 	 * locking state correctly.
7427c478bd9Sstevel@tonic-gate 	 */
7437c478bd9Sstevel@tonic-gate 	if (uap->flags & MS_REMOUNT) {
74439d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
74539d3e169Sevanl 			nfs4_free_args(args);
74639d3e169Sevanl 			kmem_free(args, sizeof (*args));
74739d3e169Sevanl 		}
7487c478bd9Sstevel@tonic-gate 		if ((mi = VFTOMI4(vfsp)) != NULL) {
7497c478bd9Sstevel@tonic-gate 			uint_t new_mi_llock;
7507c478bd9Sstevel@tonic-gate 			uint_t old_mi_llock;
7517c478bd9Sstevel@tonic-gate 			new_mi_llock = (flags & NFSMNT_LLOCK) ? 1 : 0;
7527c478bd9Sstevel@tonic-gate 			old_mi_llock = (mi->mi_flags & MI4_LLOCK) ? 1 : 0;
7537c478bd9Sstevel@tonic-gate 			if (old_mi_llock != new_mi_llock)
7547c478bd9Sstevel@tonic-gate 				return (EBUSY);
7557c478bd9Sstevel@tonic-gate 		}
7567c478bd9Sstevel@tonic-gate 		return (0);
7577c478bd9Sstevel@tonic-gate 	}
7587c478bd9Sstevel@tonic-gate 
759*b9238976Sth 	/*
760*b9238976Sth 	 * For ephemeral mount trigger stub vnodes, we have two problems
761*b9238976Sth 	 * to solve: racing threads will likely fail the v_count check, and
762*b9238976Sth 	 * we want only one to proceed with the mount.
763*b9238976Sth 	 *
764*b9238976Sth 	 * For stubs, if the mount has already occurred (via a racing thread),
765*b9238976Sth 	 * just return success. If not, skip the v_count check and proceed.
766*b9238976Sth 	 * Note that we are already serialised at this point.
767*b9238976Sth 	 */
7687c478bd9Sstevel@tonic-gate 	mutex_enter(&mvp->v_lock);
769*b9238976Sth 	if (vn_matchops(mvp, nfs4_trigger_vnodeops)) {
770*b9238976Sth 		/* mntpt is a v4 stub vnode */
771*b9238976Sth 		ASSERT(RP_ISSTUB(VTOR4(mvp)));
772*b9238976Sth 		ASSERT(!(uap->flags & MS_OVERLAY));
773*b9238976Sth 		ASSERT(!(mvp->v_flag & VROOT));
774*b9238976Sth 		if (vn_mountedvfs(mvp) != NULL) {
775*b9238976Sth 			/* ephemeral mount has already occurred */
776*b9238976Sth 			ASSERT(uap->flags & MS_SYSSPACE);
777*b9238976Sth 			mutex_exit(&mvp->v_lock);
778*b9238976Sth 			return (0);
779*b9238976Sth 		}
780*b9238976Sth 	} else {
781*b9238976Sth 		/* mntpt is a non-v4 or v4 non-stub vnode */
782*b9238976Sth 		if (!(uap->flags & MS_OVERLAY) &&
783*b9238976Sth 		    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
784*b9238976Sth 			mutex_exit(&mvp->v_lock);
785*b9238976Sth 			if (!(uap->flags & MS_SYSSPACE)) {
786*b9238976Sth 				nfs4_free_args(args);
787*b9238976Sth 				kmem_free(args, sizeof (*args));
788*b9238976Sth 			}
789*b9238976Sth 			return (EBUSY);
79039d3e169Sevanl 		}
7917c478bd9Sstevel@tonic-gate 	}
7927c478bd9Sstevel@tonic-gate 	mutex_exit(&mvp->v_lock);
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	/* make sure things are zeroed for errout: */
7957c478bd9Sstevel@tonic-gate 	rtvp = NULL;
7967c478bd9Sstevel@tonic-gate 	mi = NULL;
7977c478bd9Sstevel@tonic-gate 	secdata = NULL;
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 	/*
8007c478bd9Sstevel@tonic-gate 	 * A valid knetconfig structure is required.
8017c478bd9Sstevel@tonic-gate 	 */
80239d3e169Sevanl 	if (!(flags & NFSMNT_KNCONF) ||
803*b9238976Sth 	    args->knconf == NULL || args->knconf->knc_protofmly == NULL ||
804*b9238976Sth 	    args->knconf->knc_proto == NULL ||
805*b9238976Sth 	    (strcmp(args->knconf->knc_proto, NC_UDP) == 0)) {
80639d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
80739d3e169Sevanl 			nfs4_free_args(args);
80839d3e169Sevanl 			kmem_free(args, sizeof (*args));
80939d3e169Sevanl 		}
8107c478bd9Sstevel@tonic-gate 		return (EINVAL);
81139d3e169Sevanl 	}
81239d3e169Sevanl 
81339d3e169Sevanl 	if ((strlen(args->knconf->knc_protofmly) >= KNC_STRSIZE) ||
814*b9238976Sth 	    (strlen(args->knconf->knc_proto) >= KNC_STRSIZE)) {
81539d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
81639d3e169Sevanl 			nfs4_free_args(args);
81739d3e169Sevanl 			kmem_free(args, sizeof (*args));
81839d3e169Sevanl 		}
81939d3e169Sevanl 		return (EINVAL);
82039d3e169Sevanl 	}
82139d3e169Sevanl 
8227c478bd9Sstevel@tonic-gate 	/*
8237c478bd9Sstevel@tonic-gate 	 * Allocate a servinfo4 struct.
8247c478bd9Sstevel@tonic-gate 	 */
8257c478bd9Sstevel@tonic-gate 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
8267c478bd9Sstevel@tonic-gate 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
8277c478bd9Sstevel@tonic-gate 	if (svp_tail) {
8287c478bd9Sstevel@tonic-gate 		svp_2ndlast = svp_tail;
8297c478bd9Sstevel@tonic-gate 		svp_tail->sv_next = svp;
8307c478bd9Sstevel@tonic-gate 	} else {
8317c478bd9Sstevel@tonic-gate 		svp_head = svp;
8327c478bd9Sstevel@tonic-gate 		svp_2ndlast = svp;
8337c478bd9Sstevel@tonic-gate 	}
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	svp_tail = svp;
83639d3e169Sevanl 	svp->sv_knconf = args->knconf;
83739d3e169Sevanl 	args->knconf = NULL;
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 	/*
8407c478bd9Sstevel@tonic-gate 	 * Get server address
8417c478bd9Sstevel@tonic-gate 	 */
84239d3e169Sevanl 	if (args->addr == NULL || args->addr->buf == NULL) {
84339d3e169Sevanl 		error = EINVAL;
8447c478bd9Sstevel@tonic-gate 		goto errout;
8457c478bd9Sstevel@tonic-gate 	}
8467c478bd9Sstevel@tonic-gate 
84739d3e169Sevanl 	svp->sv_addr.maxlen = args->addr->maxlen;
84839d3e169Sevanl 	svp->sv_addr.len = args->addr->len;
84939d3e169Sevanl 	svp->sv_addr.buf = args->addr->buf;
85039d3e169Sevanl 	args->addr->buf = NULL;
85139d3e169Sevanl 
8527c478bd9Sstevel@tonic-gate 	/*
8537c478bd9Sstevel@tonic-gate 	 * Get the root fhandle
8547c478bd9Sstevel@tonic-gate 	 */
85539d3e169Sevanl 	if (args->fh == NULL || (strlen(args->fh) >= MAXPATHLEN)) {
85639d3e169Sevanl 		error = EINVAL;
8577c478bd9Sstevel@tonic-gate 		goto errout;
85839d3e169Sevanl 	}
8597c478bd9Sstevel@tonic-gate 
86039d3e169Sevanl 	svp->sv_path = args->fh;
86139d3e169Sevanl 	svp->sv_pathlen = strlen(args->fh) + 1;
86239d3e169Sevanl 	args->fh = NULL;
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	/*
8657c478bd9Sstevel@tonic-gate 	 * Get server's hostname
8667c478bd9Sstevel@tonic-gate 	 */
8677c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_HOSTNAME) {
86839d3e169Sevanl 		if (args->hostname == NULL || (strlen(args->hostname) >
869*b9238976Sth 		    MAXNETNAMELEN)) {
87039d3e169Sevanl 			error = EINVAL;
8717c478bd9Sstevel@tonic-gate 			goto errout;
87239d3e169Sevanl 		}
87339d3e169Sevanl 		svp->sv_hostnamelen = strlen(args->hostname) + 1;
87439d3e169Sevanl 		svp->sv_hostname = args->hostname;
87539d3e169Sevanl 		args->hostname = NULL;
8767c478bd9Sstevel@tonic-gate 	} else {
8777c478bd9Sstevel@tonic-gate 		char *p = "unknown-host";
87839d3e169Sevanl 		svp->sv_hostnamelen = strlen(p) + 1;
87939d3e169Sevanl 		svp->sv_hostname = kmem_zalloc(svp->sv_hostnamelen, KM_SLEEP);
88039d3e169Sevanl 		(void) strcpy(svp->sv_hostname, p);
8817c478bd9Sstevel@tonic-gate 	}
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	/*
8847c478bd9Sstevel@tonic-gate 	 * RDMA MOUNT SUPPORT FOR NFS v4.
8857c478bd9Sstevel@tonic-gate 	 * Establish, is it possible to use RDMA, if so overload the
8867c478bd9Sstevel@tonic-gate 	 * knconf with rdma specific knconf and free the orignal knconf.
8877c478bd9Sstevel@tonic-gate 	 */
8887c478bd9Sstevel@tonic-gate 	if ((flags & NFSMNT_TRYRDMA) || (flags & NFSMNT_DORDMA)) {
8897c478bd9Sstevel@tonic-gate 		/*
8907c478bd9Sstevel@tonic-gate 		 * Determine the addr type for RDMA, IPv4 or v6.
8917c478bd9Sstevel@tonic-gate 		 */
8927c478bd9Sstevel@tonic-gate 		if (strcmp(svp->sv_knconf->knc_protofmly, NC_INET) == 0)
8937c478bd9Sstevel@tonic-gate 			addr_type = AF_INET;
8947c478bd9Sstevel@tonic-gate 		else if (strcmp(svp->sv_knconf->knc_protofmly, NC_INET6) == 0)
8957c478bd9Sstevel@tonic-gate 			addr_type = AF_INET6;
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 		if (rdma_reachable(addr_type, &svp->sv_addr,
898*b9238976Sth 		    &rdma_knconf) == 0) {
8997c478bd9Sstevel@tonic-gate 			/*
9007c478bd9Sstevel@tonic-gate 			 * If successful, hijack the orignal knconf and
9017c478bd9Sstevel@tonic-gate 			 * replace with the new one, depending on the flags.
9027c478bd9Sstevel@tonic-gate 			 */
9037c478bd9Sstevel@tonic-gate 			svp->sv_origknconf = svp->sv_knconf;
9047c478bd9Sstevel@tonic-gate 			svp->sv_knconf = rdma_knconf;
9057c478bd9Sstevel@tonic-gate 		} else {
9067c478bd9Sstevel@tonic-gate 			if (flags & NFSMNT_TRYRDMA) {
9077c478bd9Sstevel@tonic-gate #ifdef	DEBUG
9087c478bd9Sstevel@tonic-gate 				if (rdma_debug)
9097c478bd9Sstevel@tonic-gate 					zcmn_err(getzoneid(), CE_WARN,
9107c478bd9Sstevel@tonic-gate 					    "no RDMA onboard, revert\n");
9117c478bd9Sstevel@tonic-gate #endif
9127c478bd9Sstevel@tonic-gate 			}
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 			if (flags & NFSMNT_DORDMA) {
9157c478bd9Sstevel@tonic-gate 				/*
9167c478bd9Sstevel@tonic-gate 				 * If proto=rdma is specified and no RDMA
9177c478bd9Sstevel@tonic-gate 				 * path to this server is avialable then
9187c478bd9Sstevel@tonic-gate 				 * ditch this server.
9197c478bd9Sstevel@tonic-gate 				 * This is not included in the mountable
9207c478bd9Sstevel@tonic-gate 				 * server list or the replica list.
9217c478bd9Sstevel@tonic-gate 				 * Check if more servers are specified;
9227c478bd9Sstevel@tonic-gate 				 * Failover case, otherwise bail out of mount.
9237c478bd9Sstevel@tonic-gate 				 */
924*b9238976Sth 				if (args->nfs_args_ext == NFS_ARGS_EXTB &&
925*b9238976Sth 				    args->nfs_ext_u.nfs_extB.next != NULL) {
92639d3e169Sevanl 					data = (char *)
927*b9238976Sth 					    args->nfs_ext_u.nfs_extB.next;
9287c478bd9Sstevel@tonic-gate 					if (uap->flags & MS_RDONLY &&
9297c478bd9Sstevel@tonic-gate 					    !(flags & NFSMNT_SOFT)) {
9307c478bd9Sstevel@tonic-gate 						if (svp_head->sv_next == NULL) {
9317c478bd9Sstevel@tonic-gate 							svp_tail = NULL;
9327c478bd9Sstevel@tonic-gate 							svp_2ndlast = NULL;
9337c478bd9Sstevel@tonic-gate 							sv4_free(svp_head);
9347c478bd9Sstevel@tonic-gate 							goto more;
9357c478bd9Sstevel@tonic-gate 						} else {
9367c478bd9Sstevel@tonic-gate 							svp_tail = svp_2ndlast;
9377c478bd9Sstevel@tonic-gate 							svp_2ndlast->sv_next =
9387c478bd9Sstevel@tonic-gate 							    NULL;
9397c478bd9Sstevel@tonic-gate 							sv4_free(svp);
9407c478bd9Sstevel@tonic-gate 							goto more;
9417c478bd9Sstevel@tonic-gate 						}
9427c478bd9Sstevel@tonic-gate 					}
9437c478bd9Sstevel@tonic-gate 				} else {
9447c478bd9Sstevel@tonic-gate 					/*
9457c478bd9Sstevel@tonic-gate 					 * This is the last server specified
9467c478bd9Sstevel@tonic-gate 					 * in the nfs_args list passed down
9477c478bd9Sstevel@tonic-gate 					 * and its not rdma capable.
9487c478bd9Sstevel@tonic-gate 					 */
9497c478bd9Sstevel@tonic-gate 					if (svp_head->sv_next == NULL) {
9507c478bd9Sstevel@tonic-gate 						/*
9517c478bd9Sstevel@tonic-gate 						 * Is this the only one
9527c478bd9Sstevel@tonic-gate 						 */
9537c478bd9Sstevel@tonic-gate 						error = EINVAL;
9547c478bd9Sstevel@tonic-gate #ifdef	DEBUG
9557c478bd9Sstevel@tonic-gate 						if (rdma_debug)
9567c478bd9Sstevel@tonic-gate 							zcmn_err(getzoneid(),
9577c478bd9Sstevel@tonic-gate 							    CE_WARN,
9587c478bd9Sstevel@tonic-gate 							    "No RDMA srv");
9597c478bd9Sstevel@tonic-gate #endif
9607c478bd9Sstevel@tonic-gate 						goto errout;
9617c478bd9Sstevel@tonic-gate 					} else {
9627c478bd9Sstevel@tonic-gate 						/*
9637c478bd9Sstevel@tonic-gate 						 * There is list, since some
9647c478bd9Sstevel@tonic-gate 						 * servers specified before
9657c478bd9Sstevel@tonic-gate 						 * this passed all requirements
9667c478bd9Sstevel@tonic-gate 						 */
9677c478bd9Sstevel@tonic-gate 						svp_tail = svp_2ndlast;
9687c478bd9Sstevel@tonic-gate 						svp_2ndlast->sv_next = NULL;
9697c478bd9Sstevel@tonic-gate 						sv4_free(svp);
9707c478bd9Sstevel@tonic-gate 						goto proceed;
9717c478bd9Sstevel@tonic-gate 					}
9727c478bd9Sstevel@tonic-gate 				}
9737c478bd9Sstevel@tonic-gate 			}
9747c478bd9Sstevel@tonic-gate 		}
9757c478bd9Sstevel@tonic-gate 	}
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 	/*
9787c478bd9Sstevel@tonic-gate 	 * If there are syncaddr and netname data, load them in. This is
9797c478bd9Sstevel@tonic-gate 	 * to support data needed for NFSV4 when AUTH_DH is the negotiated
9807c478bd9Sstevel@tonic-gate 	 * flavor via SECINFO. (instead of using MOUNT protocol in V3).
9817c478bd9Sstevel@tonic-gate 	 */
98239d3e169Sevanl 	if (args->flags & NFSMNT_SECURE) {
98339d3e169Sevanl 		svp->sv_dhsec = create_authdh_data(args->netname,
984*b9238976Sth 		    strlen(args->netname),
985*b9238976Sth 		    args->syncaddr, svp->sv_knconf);
9867c478bd9Sstevel@tonic-gate 	}
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	/*
9897c478bd9Sstevel@tonic-gate 	 * Get the extention data which has the security data structure.
9907c478bd9Sstevel@tonic-gate 	 * This includes data for AUTH_SYS as well.
9917c478bd9Sstevel@tonic-gate 	 */
9927c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_NEWARGS) {
99339d3e169Sevanl 		switch (args->nfs_args_ext) {
9947c478bd9Sstevel@tonic-gate 		case NFS_ARGS_EXTA:
9957c478bd9Sstevel@tonic-gate 		case NFS_ARGS_EXTB:
9967c478bd9Sstevel@tonic-gate 			/*
9977c478bd9Sstevel@tonic-gate 			 * Indicating the application is using the new
9987c478bd9Sstevel@tonic-gate 			 * sec_data structure to pass in the security
9997c478bd9Sstevel@tonic-gate 			 * data.
10007c478bd9Sstevel@tonic-gate 			 */
100139d3e169Sevanl 			secdata = args->nfs_ext_u.nfs_extA.secdata;
100239d3e169Sevanl 			if (secdata == NULL) {
10037c478bd9Sstevel@tonic-gate 				error = EINVAL;
100439d3e169Sevanl 			} else if (uap->flags & MS_SYSSPACE) {
100539d3e169Sevanl 				/*
100639d3e169Sevanl 				 * Need to validate the flavor here if
100739d3e169Sevanl 				 * sysspace, userspace was already
100839d3e169Sevanl 				 * validate from the nfs_copyin function.
100939d3e169Sevanl 				 */
101039d3e169Sevanl 				switch (secdata->rpcflavor) {
101139d3e169Sevanl 				case AUTH_NONE:
101239d3e169Sevanl 				case AUTH_UNIX:
101339d3e169Sevanl 				case AUTH_LOOPBACK:
101439d3e169Sevanl 				case AUTH_DES:
101539d3e169Sevanl 				case RPCSEC_GSS:
101639d3e169Sevanl 					break;
101739d3e169Sevanl 				default:
101839d3e169Sevanl 					error = EINVAL;
101939d3e169Sevanl 					goto errout;
102039d3e169Sevanl 				}
10217c478bd9Sstevel@tonic-gate 			}
102239d3e169Sevanl 			args->nfs_ext_u.nfs_extA.secdata = NULL;
10237c478bd9Sstevel@tonic-gate 			break;
10247c478bd9Sstevel@tonic-gate 
10257c478bd9Sstevel@tonic-gate 		default:
10267c478bd9Sstevel@tonic-gate 			error = EINVAL;
10277c478bd9Sstevel@tonic-gate 			break;
10287c478bd9Sstevel@tonic-gate 		}
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 	} else if (flags & NFSMNT_SECURE) {
10317c478bd9Sstevel@tonic-gate 		/*
10327c478bd9Sstevel@tonic-gate 		 * NFSMNT_SECURE is deprecated but we keep it
1033*b9238976Sth 		 * to support the rogue user-generated application
10347c478bd9Sstevel@tonic-gate 		 * that may use this undocumented interface to do
1035*b9238976Sth 		 * AUTH_DH security, e.g. our own rexd.
1036*b9238976Sth 		 *
1037*b9238976Sth 		 * Also note that NFSMNT_SECURE is used for passing
1038*b9238976Sth 		 * AUTH_DH info to be used in negotiation.
10397c478bd9Sstevel@tonic-gate 		 */
104039d3e169Sevanl 		secdata = create_authdh_data(args->netname,
1041*b9238976Sth 		    strlen(args->netname), args->syncaddr, svp->sv_knconf);
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 	} else {
10447c478bd9Sstevel@tonic-gate 		secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
10457c478bd9Sstevel@tonic-gate 		secdata->secmod = secdata->rpcflavor = AUTH_SYS;
10467c478bd9Sstevel@tonic-gate 		secdata->data = NULL;
10477c478bd9Sstevel@tonic-gate 	}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	svp->sv_secdata = secdata;
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 	/*
10527c478bd9Sstevel@tonic-gate 	 * User does not explictly specify a flavor, and a user
10537c478bd9Sstevel@tonic-gate 	 * defined default flavor is passed down.
10547c478bd9Sstevel@tonic-gate 	 */
10557c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_SECDEFAULT) {
10567c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
10577c478bd9Sstevel@tonic-gate 		svp->sv_flags |= SV4_TRYSECDEFAULT;
10587c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
10597c478bd9Sstevel@tonic-gate 	}
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 	/*
10627c478bd9Sstevel@tonic-gate 	 * Failover support:
10637c478bd9Sstevel@tonic-gate 	 *
10647c478bd9Sstevel@tonic-gate 	 * We may have a linked list of nfs_args structures,
10657c478bd9Sstevel@tonic-gate 	 * which means the user is looking for failover.  If
10667c478bd9Sstevel@tonic-gate 	 * the mount is either not "read-only" or "soft",
10677c478bd9Sstevel@tonic-gate 	 * we want to bail out with EINVAL.
10687c478bd9Sstevel@tonic-gate 	 */
106939d3e169Sevanl 	if (args->nfs_args_ext == NFS_ARGS_EXTB &&
107039d3e169Sevanl 	    args->nfs_ext_u.nfs_extB.next != NULL) {
10717c478bd9Sstevel@tonic-gate 		if (uap->flags & MS_RDONLY && !(flags & NFSMNT_SOFT)) {
107239d3e169Sevanl 			data = (char *)args->nfs_ext_u.nfs_extB.next;
10737c478bd9Sstevel@tonic-gate 			goto more;
10747c478bd9Sstevel@tonic-gate 		}
10757c478bd9Sstevel@tonic-gate 		error = EINVAL;
10767c478bd9Sstevel@tonic-gate 		goto errout;
10777c478bd9Sstevel@tonic-gate 	}
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	/*
10807c478bd9Sstevel@tonic-gate 	 * Determine the zone we're being mounted into.
10817c478bd9Sstevel@tonic-gate 	 */
108245916cd2Sjpk 	zone_hold(mntzone = zone);		/* start with this assumption */
10837c478bd9Sstevel@tonic-gate 	if (getzoneid() == GLOBAL_ZONEID) {
108445916cd2Sjpk 		zone_rele(mntzone);
10857c478bd9Sstevel@tonic-gate 		mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
10867c478bd9Sstevel@tonic-gate 		ASSERT(mntzone != NULL);
10877c478bd9Sstevel@tonic-gate 		if (mntzone != zone) {
10887c478bd9Sstevel@tonic-gate 			error = EBUSY;
10897c478bd9Sstevel@tonic-gate 			goto errout;
10907c478bd9Sstevel@tonic-gate 		}
10917c478bd9Sstevel@tonic-gate 	}
10927c478bd9Sstevel@tonic-gate 
109345916cd2Sjpk 	if (is_system_labeled()) {
109445916cd2Sjpk 		error = nfs_mount_label_policy(vfsp, &svp->sv_addr,
109545916cd2Sjpk 		    svp->sv_knconf, cr);
109645916cd2Sjpk 
109745916cd2Sjpk 		if (error > 0)
109845916cd2Sjpk 			goto errout;
109945916cd2Sjpk 
110045916cd2Sjpk 		if (error == -1) {
110145916cd2Sjpk 			/* change mount to read-only to prevent write-down */
110245916cd2Sjpk 			vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
110345916cd2Sjpk 		}
110445916cd2Sjpk 	}
110545916cd2Sjpk 
11067c478bd9Sstevel@tonic-gate 	/*
11077c478bd9Sstevel@tonic-gate 	 * Stop the mount from going any further if the zone is going away.
11087c478bd9Sstevel@tonic-gate 	 */
110945916cd2Sjpk 	if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) {
11107c478bd9Sstevel@tonic-gate 		error = EBUSY;
11117c478bd9Sstevel@tonic-gate 		goto errout;
11127c478bd9Sstevel@tonic-gate 	}
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 	/*
11157c478bd9Sstevel@tonic-gate 	 * Get root vnode.
11167c478bd9Sstevel@tonic-gate 	 */
11177c478bd9Sstevel@tonic-gate proceed:
111845916cd2Sjpk 	error = nfs4rootvp(&rtvp, vfsp, svp_head, flags, cr, mntzone);
111950a83466Sjwahlig 	if (error) {
112050a83466Sjwahlig 		/* if nfs4rootvp failed, it will free svp_head */
112150a83466Sjwahlig 		svp_head = NULL;
11227c478bd9Sstevel@tonic-gate 		goto errout;
112350a83466Sjwahlig 	}
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rtvp);
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 	/*
11287c478bd9Sstevel@tonic-gate 	 * Send client id to the server, if necessary
11297c478bd9Sstevel@tonic-gate 	 */
11307c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&n4e);
11317c478bd9Sstevel@tonic-gate 	nfs4setclientid(mi, cr, FALSE, &n4e);
1132*b9238976Sth 
11337c478bd9Sstevel@tonic-gate 	error = n4e.error;
11347c478bd9Sstevel@tonic-gate 
11357c478bd9Sstevel@tonic-gate 	if (error)
11367c478bd9Sstevel@tonic-gate 		goto errout;
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	/*
11397c478bd9Sstevel@tonic-gate 	 * Set option fields in the mount info record
11407c478bd9Sstevel@tonic-gate 	 */
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate 	if (svp_head->sv_next) {
11437c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
11447c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_LLOCK;
11457c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
11467c478bd9Sstevel@tonic-gate 	}
114739d3e169Sevanl 	error = nfs4_setopts(rtvp, DATAMODEL_NATIVE, args);
1148*b9238976Sth 	if (error)
1149*b9238976Sth 		goto errout;
1150*b9238976Sth 
1151*b9238976Sth 	/*
1152*b9238976Sth 	 * Time to tie in the mirror mount info at last!
1153*b9238976Sth 	 */
1154*b9238976Sth 	if (flags & NFSMNT_EPHEMERAL)
1155*b9238976Sth 		nfs4_record_ephemeral_mount(mi, mvp);
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate errout:
11587c478bd9Sstevel@tonic-gate 	if (error) {
11597c478bd9Sstevel@tonic-gate 		if (rtvp != NULL) {
11607c478bd9Sstevel@tonic-gate 			rp = VTOR4(rtvp);
11617c478bd9Sstevel@tonic-gate 			if (rp->r_flags & R4HASHED)
11627c478bd9Sstevel@tonic-gate 				rp4_rmhash(rp);
11637c478bd9Sstevel@tonic-gate 		}
11647c478bd9Sstevel@tonic-gate 		if (mi != NULL) {
11657c478bd9Sstevel@tonic-gate 			nfs4_async_stop(vfsp);
11667c478bd9Sstevel@tonic-gate 			nfs4_async_manager_stop(vfsp);
11677c478bd9Sstevel@tonic-gate 			nfs4_remove_mi_from_server(mi, NULL);
116850a83466Sjwahlig 			if (rtvp != NULL)
11697c478bd9Sstevel@tonic-gate 				VN_RELE(rtvp);
117045916cd2Sjpk 			if (mntzone != NULL)
117145916cd2Sjpk 				zone_rele(mntzone);
117250a83466Sjwahlig 			/* need to remove it from the zone */
117350a83466Sjwahlig 			removed = nfs4_mi_zonelist_remove(mi);
117450a83466Sjwahlig 			if (removed)
117550a83466Sjwahlig 				zone_rele(mi->mi_zone);
117650a83466Sjwahlig 			MI4_RELE(mi);
117739d3e169Sevanl 			if (!(uap->flags & MS_SYSSPACE) && args) {
117839d3e169Sevanl 				nfs4_free_args(args);
117939d3e169Sevanl 				kmem_free(args, sizeof (*args));
118039d3e169Sevanl 			}
11817c478bd9Sstevel@tonic-gate 			return (error);
11827c478bd9Sstevel@tonic-gate 		}
118350a83466Sjwahlig 		if (svp_head)
118450a83466Sjwahlig 			sv4_free(svp_head);
11857c478bd9Sstevel@tonic-gate 	}
11867c478bd9Sstevel@tonic-gate 
118739d3e169Sevanl 	if (!(uap->flags & MS_SYSSPACE) && args) {
118839d3e169Sevanl 		nfs4_free_args(args);
118939d3e169Sevanl 		kmem_free(args, sizeof (*args));
119039d3e169Sevanl 	}
11917c478bd9Sstevel@tonic-gate 	if (rtvp != NULL)
11927c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
11937c478bd9Sstevel@tonic-gate 
119445916cd2Sjpk 	if (mntzone != NULL)
119545916cd2Sjpk 		zone_rele(mntzone);
119645916cd2Sjpk 
11977c478bd9Sstevel@tonic-gate 	return (error);
11987c478bd9Sstevel@tonic-gate }
11997c478bd9Sstevel@tonic-gate 
120039d3e169Sevanl #ifdef  DEBUG
12017c478bd9Sstevel@tonic-gate #define	VERS_MSG	"NFS4 server "
12027c478bd9Sstevel@tonic-gate #else
12037c478bd9Sstevel@tonic-gate #define	VERS_MSG	"NFS server "
12047c478bd9Sstevel@tonic-gate #endif
12057c478bd9Sstevel@tonic-gate 
120639d3e169Sevanl #define	READ_MSG        \
12077c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for read transfer size"
120839d3e169Sevanl #define	WRITE_MSG       \
12097c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for write transfer size"
121039d3e169Sevanl #define	SIZE_MSG        \
12117c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for maximum file size"
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate /*
12147c478bd9Sstevel@tonic-gate  * Get the symbolic link text from the server for a given filehandle
12157c478bd9Sstevel@tonic-gate  * of that symlink.
12167c478bd9Sstevel@tonic-gate  *
121739d3e169Sevanl  *      (get symlink text) PUTFH READLINK
12187c478bd9Sstevel@tonic-gate  */
12197c478bd9Sstevel@tonic-gate static int
12207c478bd9Sstevel@tonic-gate getlinktext_otw(mntinfo4_t *mi, nfs_fh4 *fh, char **linktextp, cred_t *cr,
1221*b9238976Sth     int flags)
12227c478bd9Sstevel@tonic-gate {
12237c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
12247c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
12257c478bd9Sstevel@tonic-gate 	int doqueue;
12267c478bd9Sstevel@tonic-gate 	nfs_argop4 argop[2];
12277c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
12287c478bd9Sstevel@tonic-gate 	READLINK4res *lr_res;
12297c478bd9Sstevel@tonic-gate 	uint_t len;
12307c478bd9Sstevel@tonic-gate 	bool_t needrecov = FALSE;
12317c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
12327c478bd9Sstevel@tonic-gate 	nfs4_sharedfh_t *sfh;
12337c478bd9Sstevel@tonic-gate 	nfs4_error_t e;
12347c478bd9Sstevel@tonic-gate 	int num_retry = nfs4_max_mount_retry;
12357c478bd9Sstevel@tonic-gate 	int recovery = !(flags & NFS4_GETFH_NEEDSOP);
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	sfh = sfh4_get(fh, mi);
12387c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
12397c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
12407c478bd9Sstevel@tonic-gate 
12417c478bd9Sstevel@tonic-gate recov_retry:
12427c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&e);
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 	args.array_len = 2;
12457c478bd9Sstevel@tonic-gate 	args.array = argop;
12467c478bd9Sstevel@tonic-gate 	args.ctag = TAG_GET_SYMLINK;
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate 	if (! recovery) {
12497c478bd9Sstevel@tonic-gate 		e.error = nfs4_start_op(mi, NULL, NULL, &recov_state);
12507c478bd9Sstevel@tonic-gate 		if (e.error) {
12517c478bd9Sstevel@tonic-gate 			sfh4_rele(&sfh);
12527c478bd9Sstevel@tonic-gate 			return (e.error);
12537c478bd9Sstevel@tonic-gate 		}
12547c478bd9Sstevel@tonic-gate 	}
12557c478bd9Sstevel@tonic-gate 
12567c478bd9Sstevel@tonic-gate 	/* 0. putfh symlink fh */
12577c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_CPUTFH;
12587c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opcputfh.sfh = sfh;
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 	/* 1. readlink */
12617c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_READLINK;
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate 	doqueue = 1;
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, 0, &e);
12667c478bd9Sstevel@tonic-gate 
12677c478bd9Sstevel@tonic-gate 	needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp);
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	if (needrecov && !recovery && num_retry-- > 0) {
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
1272*b9238976Sth 		    "getlinktext_otw: initiating recovery\n"));
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate 		if (nfs4_start_recovery(&e, mi, NULL, NULL, NULL, NULL,
1275*b9238976Sth 		    OP_READLINK, NULL) == FALSE) {
1276*b9238976Sth 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
1277*b9238976Sth 			if (!e.error)
1278*b9238976Sth 				(void) xdr_free(xdr_COMPOUND4res_clnt,
1279*b9238976Sth 				    (caddr_t)&res);
12807c478bd9Sstevel@tonic-gate 			goto recov_retry;
12817c478bd9Sstevel@tonic-gate 		}
12827c478bd9Sstevel@tonic-gate 	}
12837c478bd9Sstevel@tonic-gate 
12847c478bd9Sstevel@tonic-gate 	/*
12857c478bd9Sstevel@tonic-gate 	 * If non-NFS4 pcol error and/or we weren't able to recover.
12867c478bd9Sstevel@tonic-gate 	 */
12877c478bd9Sstevel@tonic-gate 	if (e.error != 0) {
12887c478bd9Sstevel@tonic-gate 		if (! recovery)
12897c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
12907c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
12917c478bd9Sstevel@tonic-gate 		return (e.error);
12927c478bd9Sstevel@tonic-gate 	}
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 	if (res.status) {
12957c478bd9Sstevel@tonic-gate 		e.error = geterrno4(res.status);
12967c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
12977c478bd9Sstevel@tonic-gate 		if (! recovery)
12987c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
12997c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
13007c478bd9Sstevel@tonic-gate 		return (e.error);
13017c478bd9Sstevel@tonic-gate 	}
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 	/* res.status == NFS4_OK */
13047c478bd9Sstevel@tonic-gate 	ASSERT(res.status == NFS4_OK);
13057c478bd9Sstevel@tonic-gate 
130639d3e169Sevanl 	resop = &res.array[1];  /* readlink res */
13077c478bd9Sstevel@tonic-gate 	lr_res = &resop->nfs_resop4_u.opreadlink;
13087c478bd9Sstevel@tonic-gate 
13097c478bd9Sstevel@tonic-gate 	/* treat symlink name as data */
13107c478bd9Sstevel@tonic-gate 	*linktextp = utf8_to_str(&lr_res->link, &len, NULL);
13117c478bd9Sstevel@tonic-gate 
13127c478bd9Sstevel@tonic-gate 	if (! recovery)
13137c478bd9Sstevel@tonic-gate 		nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13147c478bd9Sstevel@tonic-gate 	sfh4_rele(&sfh);
13157c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
13167c478bd9Sstevel@tonic-gate 	return (0);
13177c478bd9Sstevel@tonic-gate }
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate /*
13207c478bd9Sstevel@tonic-gate  * Skip over consecutive slashes and "/./" in a pathname.
13217c478bd9Sstevel@tonic-gate  */
13227c478bd9Sstevel@tonic-gate void
13237c478bd9Sstevel@tonic-gate pathname_skipslashdot(struct pathname *pnp)
13247c478bd9Sstevel@tonic-gate {
13257c478bd9Sstevel@tonic-gate 	char *c1, *c2;
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate 	while (pnp->pn_pathlen > 0 && *pnp->pn_path == '/') {
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 		c1 = pnp->pn_path + 1;
13307c478bd9Sstevel@tonic-gate 		c2 = pnp->pn_path + 2;
13317c478bd9Sstevel@tonic-gate 
13327c478bd9Sstevel@tonic-gate 		if (*c1 == '.' && (*c2 == '/' || *c2 == '\0')) {
13337c478bd9Sstevel@tonic-gate 			pnp->pn_path = pnp->pn_path + 2; /* skip "/." */
13347c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen = pnp->pn_pathlen - 2;
13357c478bd9Sstevel@tonic-gate 		} else {
13367c478bd9Sstevel@tonic-gate 			pnp->pn_path++;
13377c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen--;
13387c478bd9Sstevel@tonic-gate 		}
13397c478bd9Sstevel@tonic-gate 	}
13407c478bd9Sstevel@tonic-gate }
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate /*
13437c478bd9Sstevel@tonic-gate  * Resolve a symbolic link path. The symlink is in the nth component of
13447c478bd9Sstevel@tonic-gate  * svp->sv_path and has an nfs4 file handle "fh".
13457c478bd9Sstevel@tonic-gate  * Upon return, the sv_path will point to the new path that has the nth
13467c478bd9Sstevel@tonic-gate  * component resolved to its symlink text.
13477c478bd9Sstevel@tonic-gate  */
13487c478bd9Sstevel@tonic-gate int
13497c478bd9Sstevel@tonic-gate resolve_sympath(mntinfo4_t *mi, servinfo4_t *svp, int nth, nfs_fh4 *fh,
1350*b9238976Sth     cred_t *cr, int flags)
13517c478bd9Sstevel@tonic-gate {
13527c478bd9Sstevel@tonic-gate 	char *oldpath;
13537c478bd9Sstevel@tonic-gate 	char *symlink, *newpath;
13547c478bd9Sstevel@tonic-gate 	struct pathname oldpn, newpn;
13557c478bd9Sstevel@tonic-gate 	char component[MAXNAMELEN];
13567c478bd9Sstevel@tonic-gate 	int i, addlen, error = 0;
13577c478bd9Sstevel@tonic-gate 	int oldpathlen;
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	/* Get the symbolic link text over the wire. */
13607c478bd9Sstevel@tonic-gate 	error = getlinktext_otw(mi, fh, &symlink, cr, flags);
13617c478bd9Sstevel@tonic-gate 
13627c478bd9Sstevel@tonic-gate 	if (error || symlink == NULL || strlen(symlink) == 0)
13637c478bd9Sstevel@tonic-gate 		return (error);
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate 	/*
13667c478bd9Sstevel@tonic-gate 	 * Compose the new pathname.
13677c478bd9Sstevel@tonic-gate 	 * Note:
13687c478bd9Sstevel@tonic-gate 	 *    - only the nth component is resolved for the pathname.
13697c478bd9Sstevel@tonic-gate 	 *    - pathname.pn_pathlen does not count the ending null byte.
13707c478bd9Sstevel@tonic-gate 	 */
13717c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
13727c478bd9Sstevel@tonic-gate 	oldpath = svp->sv_path;
13737c478bd9Sstevel@tonic-gate 	oldpathlen = svp->sv_pathlen;
13747c478bd9Sstevel@tonic-gate 	if (error = pn_get(oldpath, UIO_SYSSPACE, &oldpn)) {
13757c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
13767c478bd9Sstevel@tonic-gate 		kmem_free(symlink, strlen(symlink) + 1);
13777c478bd9Sstevel@tonic-gate 		return (error);
13787c478bd9Sstevel@tonic-gate 	}
13797c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
13807c478bd9Sstevel@tonic-gate 	pn_alloc(&newpn);
13817c478bd9Sstevel@tonic-gate 
13827c478bd9Sstevel@tonic-gate 	/*
13837c478bd9Sstevel@tonic-gate 	 * Skip over previous components from the oldpath so that the
13847c478bd9Sstevel@tonic-gate 	 * oldpn.pn_path will point to the symlink component. Skip
13857c478bd9Sstevel@tonic-gate 	 * leading slashes and "/./" (no OP_LOOKUP on ".") so that
13867c478bd9Sstevel@tonic-gate 	 * pn_getcompnent can get the component.
13877c478bd9Sstevel@tonic-gate 	 */
13887c478bd9Sstevel@tonic-gate 	for (i = 1; i < nth; i++) {
13897c478bd9Sstevel@tonic-gate 		pathname_skipslashdot(&oldpn);
13907c478bd9Sstevel@tonic-gate 		error = pn_getcomponent(&oldpn, component);
13917c478bd9Sstevel@tonic-gate 		if (error)
13927c478bd9Sstevel@tonic-gate 			goto out;
13937c478bd9Sstevel@tonic-gate 	}
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 	/*
13967c478bd9Sstevel@tonic-gate 	 * Copy the old path upto the component right before the symlink
13977c478bd9Sstevel@tonic-gate 	 * if the symlink is not an absolute path.
13987c478bd9Sstevel@tonic-gate 	 */
13997c478bd9Sstevel@tonic-gate 	if (symlink[0] != '/') {
14007c478bd9Sstevel@tonic-gate 		addlen = oldpn.pn_path - oldpn.pn_buf;
14017c478bd9Sstevel@tonic-gate 		bcopy(oldpn.pn_buf, newpn.pn_path, addlen);
14027c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen += addlen;
14037c478bd9Sstevel@tonic-gate 		newpn.pn_path += addlen;
14047c478bd9Sstevel@tonic-gate 		newpn.pn_buf[newpn.pn_pathlen] = '/';
14057c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen++;
14067c478bd9Sstevel@tonic-gate 		newpn.pn_path++;
14077c478bd9Sstevel@tonic-gate 	}
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 	/* copy the resolved symbolic link text */
14107c478bd9Sstevel@tonic-gate 	addlen = strlen(symlink);
14117c478bd9Sstevel@tonic-gate 	if (newpn.pn_pathlen + addlen >= newpn.pn_bufsize) {
14127c478bd9Sstevel@tonic-gate 		error = ENAMETOOLONG;
14137c478bd9Sstevel@tonic-gate 		goto out;
14147c478bd9Sstevel@tonic-gate 	}
14157c478bd9Sstevel@tonic-gate 	bcopy(symlink, newpn.pn_path, addlen);
14167c478bd9Sstevel@tonic-gate 	newpn.pn_pathlen += addlen;
14177c478bd9Sstevel@tonic-gate 	newpn.pn_path += addlen;
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 	/*
14207c478bd9Sstevel@tonic-gate 	 * Check if there is any remaining path after the symlink component.
14217c478bd9Sstevel@tonic-gate 	 * First, skip the symlink component.
14227c478bd9Sstevel@tonic-gate 	 */
14237c478bd9Sstevel@tonic-gate 	pathname_skipslashdot(&oldpn);
14247c478bd9Sstevel@tonic-gate 	if (error = pn_getcomponent(&oldpn, component))
14257c478bd9Sstevel@tonic-gate 		goto out;
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 	addlen = pn_pathleft(&oldpn); /* includes counting the slash */
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 	/*
14307c478bd9Sstevel@tonic-gate 	 * Copy the remaining path to the new pathname if there is any.
14317c478bd9Sstevel@tonic-gate 	 */
14327c478bd9Sstevel@tonic-gate 	if (addlen > 0) {
14337c478bd9Sstevel@tonic-gate 		if (newpn.pn_pathlen + addlen >= newpn.pn_bufsize) {
14347c478bd9Sstevel@tonic-gate 			error = ENAMETOOLONG;
14357c478bd9Sstevel@tonic-gate 			goto out;
14367c478bd9Sstevel@tonic-gate 		}
14377c478bd9Sstevel@tonic-gate 		bcopy(oldpn.pn_path, newpn.pn_path, addlen);
14387c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen += addlen;
14397c478bd9Sstevel@tonic-gate 	}
14407c478bd9Sstevel@tonic-gate 	newpn.pn_buf[newpn.pn_pathlen] = '\0';
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	/* get the newpath and store it in the servinfo4_t */
14437c478bd9Sstevel@tonic-gate 	newpath = kmem_alloc(newpn.pn_pathlen + 1, KM_SLEEP);
14447c478bd9Sstevel@tonic-gate 	bcopy(newpn.pn_buf, newpath, newpn.pn_pathlen);
14457c478bd9Sstevel@tonic-gate 	newpath[newpn.pn_pathlen] = '\0';
14467c478bd9Sstevel@tonic-gate 
14477c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
14487c478bd9Sstevel@tonic-gate 	svp->sv_path = newpath;
14497c478bd9Sstevel@tonic-gate 	svp->sv_pathlen = strlen(newpath) + 1;
14507c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	kmem_free(oldpath, oldpathlen);
14537c478bd9Sstevel@tonic-gate out:
14547c478bd9Sstevel@tonic-gate 	kmem_free(symlink, strlen(symlink) + 1);
14557c478bd9Sstevel@tonic-gate 	pn_free(&newpn);
14567c478bd9Sstevel@tonic-gate 	pn_free(&oldpn);
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	return (error);
14597c478bd9Sstevel@tonic-gate }
14607c478bd9Sstevel@tonic-gate 
14617c478bd9Sstevel@tonic-gate /*
14627c478bd9Sstevel@tonic-gate  * Get the root filehandle for the given filesystem and server, and update
14637c478bd9Sstevel@tonic-gate  * svp.
14647c478bd9Sstevel@tonic-gate  *
14657c478bd9Sstevel@tonic-gate  * If NFS4_GETFH_NEEDSOP is set, then use nfs4_start_fop and nfs4_end_fop
14667c478bd9Sstevel@tonic-gate  * to coordinate with recovery.  Otherwise, the caller is assumed to be
14677c478bd9Sstevel@tonic-gate  * the recovery thread or have already done a start_fop.
14687c478bd9Sstevel@tonic-gate  *
14697c478bd9Sstevel@tonic-gate  * Errors are returned by the nfs4_error_t parameter.
14707c478bd9Sstevel@tonic-gate  */
14717c478bd9Sstevel@tonic-gate 
14727c478bd9Sstevel@tonic-gate static void
14737c478bd9Sstevel@tonic-gate nfs4getfh_otw(struct mntinfo4 *mi, servinfo4_t *svp, vtype_t *vtp,
1474*b9238976Sth     int flags, cred_t *cr, nfs4_error_t *ep)
14757c478bd9Sstevel@tonic-gate {
14767c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
14777c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
14787c478bd9Sstevel@tonic-gate 	int doqueue = 1;
14797c478bd9Sstevel@tonic-gate 	nfs_argop4 *argop;
14807c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
14817c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t *garp;
14827c478bd9Sstevel@tonic-gate 	int num_argops;
14837c478bd9Sstevel@tonic-gate 	lookup4_param_t lookuparg;
14847c478bd9Sstevel@tonic-gate 	nfs_fh4 *tmpfhp;
14857c478bd9Sstevel@tonic-gate 	nfs_fh4 *resfhp;
14867c478bd9Sstevel@tonic-gate 	bool_t needrecov = FALSE;
14877c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
14887c478bd9Sstevel@tonic-gate 	int llndx;
14897c478bd9Sstevel@tonic-gate 	int nthcomp;
14907c478bd9Sstevel@tonic-gate 	int recovery = !(flags & NFS4_GETFH_NEEDSOP);
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
14937c478bd9Sstevel@tonic-gate 	ASSERT(svp->sv_path != NULL);
14947c478bd9Sstevel@tonic-gate 	if (svp->sv_path[0] == '\0') {
14957c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
14967c478bd9Sstevel@tonic-gate 		nfs4_error_init(ep, EINVAL);
14977c478bd9Sstevel@tonic-gate 		return;
14987c478bd9Sstevel@tonic-gate 	}
14997c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
15027c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
15037c478bd9Sstevel@tonic-gate recov_retry:
15047c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(ep);
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate 	if (!recovery) {
15077c478bd9Sstevel@tonic-gate 		ep->error = nfs4_start_fop(mi, NULL, NULL, OH_MOUNT,
1508*b9238976Sth 		    &recov_state, NULL);
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate 		/*
15117c478bd9Sstevel@tonic-gate 		 * If recovery has been started and this request as
15127c478bd9Sstevel@tonic-gate 		 * initiated by a mount, then we must wait for recovery
15137c478bd9Sstevel@tonic-gate 		 * to finish before proceeding, otherwise, the error
15147c478bd9Sstevel@tonic-gate 		 * cleanup would remove data structures needed by the
15157c478bd9Sstevel@tonic-gate 		 * recovery thread.
15167c478bd9Sstevel@tonic-gate 		 */
15177c478bd9Sstevel@tonic-gate 		if (ep->error) {
15187c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
15197c478bd9Sstevel@tonic-gate 			if (mi->mi_flags & MI4_MOUNTING) {
15207c478bd9Sstevel@tonic-gate 				mi->mi_flags |= MI4_RECOV_FAIL;
15217c478bd9Sstevel@tonic-gate 				mi->mi_error = EIO;
15227c478bd9Sstevel@tonic-gate 
15237c478bd9Sstevel@tonic-gate 				NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
15247c478bd9Sstevel@tonic-gate 				    "nfs4getfh_otw: waiting 4 recovery\n"));
15257c478bd9Sstevel@tonic-gate 
15267c478bd9Sstevel@tonic-gate 				while (mi->mi_flags & MI4_RECOV_ACTIV)
15277c478bd9Sstevel@tonic-gate 					cv_wait(&mi->mi_failover_cv,
15287c478bd9Sstevel@tonic-gate 					    &mi->mi_lock);
15297c478bd9Sstevel@tonic-gate 			}
15307c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
15317c478bd9Sstevel@tonic-gate 			return;
15327c478bd9Sstevel@tonic-gate 		}
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate 		/*
15357c478bd9Sstevel@tonic-gate 		 * If the client does not specify a specific flavor to use
15367c478bd9Sstevel@tonic-gate 		 * and has not gotten a secinfo list from the server yet,
15377c478bd9Sstevel@tonic-gate 		 * retrieve the secinfo list from the server and use a
15387c478bd9Sstevel@tonic-gate 		 * flavor from the list to mount.
15397c478bd9Sstevel@tonic-gate 		 *
15407c478bd9Sstevel@tonic-gate 		 * If fail to get the secinfo list from the server, then
15417c478bd9Sstevel@tonic-gate 		 * try the default flavor.
15427c478bd9Sstevel@tonic-gate 		 */
15437c478bd9Sstevel@tonic-gate 		if ((svp->sv_flags & SV4_TRYSECDEFAULT) &&
15447c478bd9Sstevel@tonic-gate 		    svp->sv_secinfo == NULL) {
15457c478bd9Sstevel@tonic-gate 			(void) nfs4_secinfo_path(mi, cr, FALSE);
15467c478bd9Sstevel@tonic-gate 		}
15477c478bd9Sstevel@tonic-gate 	}
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 	if (recovery)
15507c478bd9Sstevel@tonic-gate 		args.ctag = TAG_REMAP_MOUNT;
15517c478bd9Sstevel@tonic-gate 	else
15527c478bd9Sstevel@tonic-gate 		args.ctag = TAG_MOUNT;
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate 	lookuparg.l4_getattrs = LKP4_ALL_ATTRIBUTES;
15557c478bd9Sstevel@tonic-gate 	lookuparg.argsp = &args;
15567c478bd9Sstevel@tonic-gate 	lookuparg.resp = &res;
15577c478bd9Sstevel@tonic-gate 	lookuparg.header_len = 2;	/* Putrootfh, getfh */
15587c478bd9Sstevel@tonic-gate 	lookuparg.trailer_len = 0;
15597c478bd9Sstevel@tonic-gate 	lookuparg.ga_bits = FATTR4_FSINFO_MASK;
15607c478bd9Sstevel@tonic-gate 	lookuparg.mi = mi;
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
15637c478bd9Sstevel@tonic-gate 	ASSERT(svp->sv_path != NULL);
15647c478bd9Sstevel@tonic-gate 	llndx = nfs4lookup_setup(svp->sv_path, &lookuparg, 0);
15657c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate 	argop = args.array;
15687c478bd9Sstevel@tonic-gate 	num_argops = args.array_len;
15697c478bd9Sstevel@tonic-gate 
15707c478bd9Sstevel@tonic-gate 	/* choose public or root filehandle */
15717c478bd9Sstevel@tonic-gate 	if (flags & NFS4_GETFH_PUBLIC)
15727c478bd9Sstevel@tonic-gate 		argop[0].argop = OP_PUTPUBFH;
15737c478bd9Sstevel@tonic-gate 	else
15747c478bd9Sstevel@tonic-gate 		argop[0].argop = OP_PUTROOTFH;
15757c478bd9Sstevel@tonic-gate 
15767c478bd9Sstevel@tonic-gate 	/* get fh */
15777c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_GETFH;
15787c478bd9Sstevel@tonic-gate 
15797c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE,
15807c478bd9Sstevel@tonic-gate 	    "nfs4getfh_otw: %s call, mi 0x%p",
15817c478bd9Sstevel@tonic-gate 	    needrecov ? "recov" : "first", (void *)mi));
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, ep);
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate 	needrecov = nfs4_needs_recovery(ep, FALSE, mi->mi_vfsp);
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate 	if (needrecov) {
15887c478bd9Sstevel@tonic-gate 		bool_t abort;
15897c478bd9Sstevel@tonic-gate 
15907c478bd9Sstevel@tonic-gate 		if (recovery) {
15917c478bd9Sstevel@tonic-gate 			nfs4args_lookup_free(argop, num_argops);
15927c478bd9Sstevel@tonic-gate 			kmem_free(argop,
1593*b9238976Sth 			    lookuparg.arglen * sizeof (nfs_argop4));
15947c478bd9Sstevel@tonic-gate 			if (!ep->error)
15957c478bd9Sstevel@tonic-gate 				(void) xdr_free(xdr_COMPOUND4res_clnt,
1596*b9238976Sth 				    (caddr_t)&res);
15977c478bd9Sstevel@tonic-gate 			return;
15987c478bd9Sstevel@tonic-gate 		}
15997c478bd9Sstevel@tonic-gate 
16007c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_recov_debug,
16017c478bd9Sstevel@tonic-gate 		    (CE_NOTE, "nfs4getfh_otw: initiating recovery\n"));
16027c478bd9Sstevel@tonic-gate 
16037c478bd9Sstevel@tonic-gate 		abort = nfs4_start_recovery(ep, mi, NULL,
1604*b9238976Sth 		    NULL, NULL, NULL, OP_GETFH, NULL);
16057c478bd9Sstevel@tonic-gate 		if (!ep->error) {
16067c478bd9Sstevel@tonic-gate 			ep->error = geterrno4(res.status);
16077c478bd9Sstevel@tonic-gate 			(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
16087c478bd9Sstevel@tonic-gate 		}
16097c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
16107c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
16117c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
16127c478bd9Sstevel@tonic-gate 		/* have another go? */
16137c478bd9Sstevel@tonic-gate 		if (abort == FALSE)
16147c478bd9Sstevel@tonic-gate 			goto recov_retry;
16157c478bd9Sstevel@tonic-gate 		return;
16167c478bd9Sstevel@tonic-gate 	}
16177c478bd9Sstevel@tonic-gate 
16187c478bd9Sstevel@tonic-gate 	/*
16197c478bd9Sstevel@tonic-gate 	 * No recovery, but check if error is set.
16207c478bd9Sstevel@tonic-gate 	 */
16217c478bd9Sstevel@tonic-gate 	if (ep->error)  {
16227c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
16237c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
16247c478bd9Sstevel@tonic-gate 		if (!recovery)
16257c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1626*b9238976Sth 			    needrecov);
16277c478bd9Sstevel@tonic-gate 		return;
16287c478bd9Sstevel@tonic-gate 	}
16297c478bd9Sstevel@tonic-gate 
16307c478bd9Sstevel@tonic-gate is_link_err:
16317c478bd9Sstevel@tonic-gate 
16327c478bd9Sstevel@tonic-gate 	/* for non-recovery errors */
16337c478bd9Sstevel@tonic-gate 	if (res.status && res.status != NFS4ERR_SYMLINK) {
16347c478bd9Sstevel@tonic-gate 		if (!recovery) {
16357c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1636*b9238976Sth 			    needrecov);
16377c478bd9Sstevel@tonic-gate 		}
16387c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
16397c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
16407c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
16417c478bd9Sstevel@tonic-gate 		return;
16427c478bd9Sstevel@tonic-gate 	}
16437c478bd9Sstevel@tonic-gate 
16447c478bd9Sstevel@tonic-gate 	/*
16457c478bd9Sstevel@tonic-gate 	 * If any intermediate component in the path is a symbolic link,
16467c478bd9Sstevel@tonic-gate 	 * resolve the symlink, then try mount again using the new path.
16477c478bd9Sstevel@tonic-gate 	 */
16487c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_SYMLINK) {
16497c478bd9Sstevel@tonic-gate 		int where;
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 		/*
16527c478bd9Sstevel@tonic-gate 		 * This must be from OP_LOOKUP failure. The (cfh) for this
16537c478bd9Sstevel@tonic-gate 		 * OP_LOOKUP is a symlink node. Found out where the
16547c478bd9Sstevel@tonic-gate 		 * OP_GETFH is for the (cfh) that is a symlink node.
16557c478bd9Sstevel@tonic-gate 		 *
16567c478bd9Sstevel@tonic-gate 		 * Example:
16577c478bd9Sstevel@tonic-gate 		 * (mount) PUTROOTFH, GETFH, LOOKUP comp1, GETFH, GETATTR,
16587c478bd9Sstevel@tonic-gate 		 * LOOKUP comp2, GETFH, GETATTR, LOOKUP comp3, GETFH, GETATTR
16597c478bd9Sstevel@tonic-gate 		 *
16607c478bd9Sstevel@tonic-gate 		 * LOOKUP comp3 fails with SYMLINK because comp2 is a symlink.
16617c478bd9Sstevel@tonic-gate 		 * In this case, where = 7, nthcomp = 2.
16627c478bd9Sstevel@tonic-gate 		 */
16637c478bd9Sstevel@tonic-gate 		where = res.array_len - 2;
16647c478bd9Sstevel@tonic-gate 		ASSERT(where > 0);
16657c478bd9Sstevel@tonic-gate 
16667c478bd9Sstevel@tonic-gate 		resop = &res.array[where - 1];
16677c478bd9Sstevel@tonic-gate 		ASSERT(resop->resop == OP_GETFH);
16687c478bd9Sstevel@tonic-gate 		tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
16697c478bd9Sstevel@tonic-gate 		nthcomp = res.array_len/3 - 1;
16707c478bd9Sstevel@tonic-gate 
16717c478bd9Sstevel@tonic-gate 		/*
16727c478bd9Sstevel@tonic-gate 		 * Need to call nfs4_end_op before resolve_sympath to avoid
16737c478bd9Sstevel@tonic-gate 		 * potential nfs4_start_op deadlock.
16747c478bd9Sstevel@tonic-gate 		 */
16757c478bd9Sstevel@tonic-gate 		if (!recovery)
16767c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1677*b9238976Sth 			    needrecov);
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 		ep->error = resolve_sympath(mi, svp, nthcomp, tmpfhp, cr,
1680*b9238976Sth 		    flags);
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
16837c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
16847c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 		if (ep->error)
16877c478bd9Sstevel@tonic-gate 			return;
16887c478bd9Sstevel@tonic-gate 
16897c478bd9Sstevel@tonic-gate 		goto recov_retry;
16907c478bd9Sstevel@tonic-gate 	}
16917c478bd9Sstevel@tonic-gate 
16927c478bd9Sstevel@tonic-gate 	/* getfh */
16937c478bd9Sstevel@tonic-gate 	resop = &res.array[res.array_len - 2];
16947c478bd9Sstevel@tonic-gate 	ASSERT(resop->resop == OP_GETFH);
16957c478bd9Sstevel@tonic-gate 	resfhp = &resop->nfs_resop4_u.opgetfh.object;
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate 	/* getattr fsinfo res */
16987c478bd9Sstevel@tonic-gate 	resop++;
16997c478bd9Sstevel@tonic-gate 	garp = &resop->nfs_resop4_u.opgetattr.ga_res;
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 	*vtp = garp->n4g_va.va_type;
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate 	mi->mi_fh_expire_type = garp->n4g_ext_res->n4g_fet;
17047c478bd9Sstevel@tonic-gate 
17057c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
17067c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_pc4.pc4_link_support)
17077c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_LINK;
17087c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_pc4.pc4_symlink_support)
17097c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_SYMLINK;
17107c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_suppattrs & FATTR4_ACL_MASK)
17117c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_ACL;
17127c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
17137c478bd9Sstevel@tonic-gate 
17147c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxread == 0)
17157c478bd9Sstevel@tonic-gate 		mi->mi_tsize =
1716*b9238976Sth 		    MIN(MAXBSIZE, mi->mi_tsize);
17177c478bd9Sstevel@tonic-gate 	else
17187c478bd9Sstevel@tonic-gate 		mi->mi_tsize =
1719*b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxread,
1720*b9238976Sth 		    mi->mi_tsize);
17217c478bd9Sstevel@tonic-gate 
17227c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxwrite == 0)
17237c478bd9Sstevel@tonic-gate 		mi->mi_stsize =
1724*b9238976Sth 		    MIN(MAXBSIZE, mi->mi_stsize);
17257c478bd9Sstevel@tonic-gate 	else
17267c478bd9Sstevel@tonic-gate 		mi->mi_stsize =
1727*b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxwrite,
1728*b9238976Sth 		    mi->mi_stsize);
17297c478bd9Sstevel@tonic-gate 
17307c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxfilesize != 0)
17317c478bd9Sstevel@tonic-gate 		mi->mi_maxfilesize =
1732*b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxfilesize,
1733*b9238976Sth 		    mi->mi_maxfilesize);
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate 	/*
17367c478bd9Sstevel@tonic-gate 	 * If the final component is a a symbolic link, resolve the symlink,
17377c478bd9Sstevel@tonic-gate 	 * then try mount again using the new path.
17387c478bd9Sstevel@tonic-gate 	 *
17397c478bd9Sstevel@tonic-gate 	 * Assume no symbolic link for root filesysm "/".
17407c478bd9Sstevel@tonic-gate 	 */
17417c478bd9Sstevel@tonic-gate 	if (*vtp == VLNK) {
17427c478bd9Sstevel@tonic-gate 		/*
17437c478bd9Sstevel@tonic-gate 		 * nthcomp is the total result length minus
17447c478bd9Sstevel@tonic-gate 		 * the 1st 2 OPs (PUTROOTFH, GETFH),
17457c478bd9Sstevel@tonic-gate 		 * then divided by 3 (LOOKUP,GETFH,GETATTR)
17467c478bd9Sstevel@tonic-gate 		 *
17477c478bd9Sstevel@tonic-gate 		 * e.g. PUTROOTFH GETFH LOOKUP 1st-comp GETFH GETATTR
17487c478bd9Sstevel@tonic-gate 		 *	LOOKUP 2nd-comp GETFH GETATTR
17497c478bd9Sstevel@tonic-gate 		 *
17507c478bd9Sstevel@tonic-gate 		 *	(8 - 2)/3 = 2
17517c478bd9Sstevel@tonic-gate 		 */
17527c478bd9Sstevel@tonic-gate 		nthcomp = (res.array_len - 2)/3;
17537c478bd9Sstevel@tonic-gate 
17547c478bd9Sstevel@tonic-gate 		/*
17557c478bd9Sstevel@tonic-gate 		 * Need to call nfs4_end_op before resolve_sympath to avoid
17567c478bd9Sstevel@tonic-gate 		 * potential nfs4_start_op deadlock. See RFE 4777612.
17577c478bd9Sstevel@tonic-gate 		 */
17587c478bd9Sstevel@tonic-gate 		if (!recovery)
17597c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1760*b9238976Sth 			    needrecov);
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 		ep->error = resolve_sympath(mi, svp, nthcomp, resfhp, cr,
1763*b9238976Sth 		    flags);
17647c478bd9Sstevel@tonic-gate 
17657c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
17667c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
17677c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 		if (ep->error)
17707c478bd9Sstevel@tonic-gate 			return;
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate 		goto recov_retry;
17737c478bd9Sstevel@tonic-gate 	}
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 	/*
17767c478bd9Sstevel@tonic-gate 	 * We need to figure out where in the compound the getfh
17777c478bd9Sstevel@tonic-gate 	 * for the parent directory is. If the object to be mounted is
17787c478bd9Sstevel@tonic-gate 	 * the root, then there is no lookup at all:
17797c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH.
17807c478bd9Sstevel@tonic-gate 	 * If the object to be mounted is in the root, then the compound is:
17817c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH, LOOKUP, GETFH, GETATTR.
17827c478bd9Sstevel@tonic-gate 	 * In either of these cases, the index of the GETFH is 1.
17837c478bd9Sstevel@tonic-gate 	 * If it is not at the root, then it's something like:
17847c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH, LOOKUP, GETFH, GETATTR,
17857c478bd9Sstevel@tonic-gate 	 * LOOKUP, GETFH, GETATTR
17867c478bd9Sstevel@tonic-gate 	 * In this case, the index is llndx (last lookup index) - 2.
17877c478bd9Sstevel@tonic-gate 	 */
17887c478bd9Sstevel@tonic-gate 	if (llndx == -1 || llndx == 2)
17897c478bd9Sstevel@tonic-gate 		resop = &res.array[1];
17907c478bd9Sstevel@tonic-gate 	else {
17917c478bd9Sstevel@tonic-gate 		ASSERT(llndx > 2);
17927c478bd9Sstevel@tonic-gate 		resop = &res.array[llndx-2];
17937c478bd9Sstevel@tonic-gate 	}
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 	ASSERT(resop->resop == OP_GETFH);
17967c478bd9Sstevel@tonic-gate 	tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 	/* save the filehandles for the replica */
17997c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
18007c478bd9Sstevel@tonic-gate 	ASSERT(tmpfhp->nfs_fh4_len <= NFS4_FHSIZE);
18017c478bd9Sstevel@tonic-gate 	svp->sv_pfhandle.fh_len = tmpfhp->nfs_fh4_len;
18027c478bd9Sstevel@tonic-gate 	bcopy(tmpfhp->nfs_fh4_val, svp->sv_pfhandle.fh_buf,
18037c478bd9Sstevel@tonic-gate 	    tmpfhp->nfs_fh4_len);
18047c478bd9Sstevel@tonic-gate 	ASSERT(resfhp->nfs_fh4_len <= NFS4_FHSIZE);
18057c478bd9Sstevel@tonic-gate 	svp->sv_fhandle.fh_len = resfhp->nfs_fh4_len;
18067c478bd9Sstevel@tonic-gate 	bcopy(resfhp->nfs_fh4_val, svp->sv_fhandle.fh_buf, resfhp->nfs_fh4_len);
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 	/* initialize fsid and supp_attrs for server fs */
18097c478bd9Sstevel@tonic-gate 	svp->sv_fsid = garp->n4g_fsid;
18107c478bd9Sstevel@tonic-gate 	svp->sv_supp_attrs =
1811*b9238976Sth 	    garp->n4g_ext_res->n4g_suppattrs | FATTR4_MANDATTR_MASK;
18127c478bd9Sstevel@tonic-gate 
18137c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
18147c478bd9Sstevel@tonic-gate 
18157c478bd9Sstevel@tonic-gate 	nfs4args_lookup_free(argop, num_argops);
18167c478bd9Sstevel@tonic-gate 	kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18177c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
18187c478bd9Sstevel@tonic-gate 	if (!recovery)
18197c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
18207c478bd9Sstevel@tonic-gate }
18217c478bd9Sstevel@tonic-gate 
18227c478bd9Sstevel@tonic-gate static ushort_t nfs4_max_threads = 8;	/* max number of active async threads */
18237c478bd9Sstevel@tonic-gate static uint_t nfs4_bsize = 32 * 1024;	/* client `block' size */
18247c478bd9Sstevel@tonic-gate static uint_t nfs4_async_clusters = 1;	/* # of reqs from each async queue */
18257c478bd9Sstevel@tonic-gate static uint_t nfs4_cots_timeo = NFS_COTS_TIMEO;
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate /*
18287c478bd9Sstevel@tonic-gate  * Remap the root filehandle for the given filesystem.
18297c478bd9Sstevel@tonic-gate  *
18307c478bd9Sstevel@tonic-gate  * results returned via the nfs4_error_t parameter.
18317c478bd9Sstevel@tonic-gate  */
18327c478bd9Sstevel@tonic-gate void
18337c478bd9Sstevel@tonic-gate nfs4_remap_root(mntinfo4_t *mi, nfs4_error_t *ep, int flags)
18347c478bd9Sstevel@tonic-gate {
18357c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp;
18367c478bd9Sstevel@tonic-gate 	vtype_t vtype;
18377c478bd9Sstevel@tonic-gate 	nfs_fh4 rootfh;
18387c478bd9Sstevel@tonic-gate 	int getfh_flags;
18397c478bd9Sstevel@tonic-gate 	char *orig_sv_path;
18407c478bd9Sstevel@tonic-gate 	int orig_sv_pathlen, num_retry;
18417c478bd9Sstevel@tonic-gate 
18427c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
18438c9e5ad2Saalok 
18448c9e5ad2Saalok remap_retry:
18457c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
18467c478bd9Sstevel@tonic-gate 	getfh_flags =
1847*b9238976Sth 	    (flags & NFS4_REMAP_NEEDSOP) ? NFS4_GETFH_NEEDSOP : 0;
18487c478bd9Sstevel@tonic-gate 	getfh_flags |=
1849*b9238976Sth 	    (mi->mi_flags & MI4_PUBLIC) ? NFS4_GETFH_PUBLIC : 0;
18507c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
18517c478bd9Sstevel@tonic-gate 
18527c478bd9Sstevel@tonic-gate 	/*
18537c478bd9Sstevel@tonic-gate 	 * Just in case server path being mounted contains
18547c478bd9Sstevel@tonic-gate 	 * symlinks and fails w/STALE, save the initial sv_path
18557c478bd9Sstevel@tonic-gate 	 * so we can redrive the initial mount compound with the
18567c478bd9Sstevel@tonic-gate 	 * initial sv_path -- not a symlink-expanded version.
18577c478bd9Sstevel@tonic-gate 	 *
18587c478bd9Sstevel@tonic-gate 	 * This could only happen if a symlink was expanded
18597c478bd9Sstevel@tonic-gate 	 * and the expanded mount compound failed stale.  Because
18607c478bd9Sstevel@tonic-gate 	 * it could be the case that the symlink was removed at
18617c478bd9Sstevel@tonic-gate 	 * the server (and replaced with another symlink/dir,
18627c478bd9Sstevel@tonic-gate 	 * we need to use the initial sv_path when attempting
18637c478bd9Sstevel@tonic-gate 	 * to re-lookup everything and recover.
18647c478bd9Sstevel@tonic-gate 	 */
18657c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
18667c478bd9Sstevel@tonic-gate 	orig_sv_pathlen = svp->sv_pathlen;
18677c478bd9Sstevel@tonic-gate 	orig_sv_path = kmem_alloc(orig_sv_pathlen, KM_SLEEP);
18687c478bd9Sstevel@tonic-gate 	bcopy(svp->sv_path, orig_sv_path, orig_sv_pathlen);
18697c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
18707c478bd9Sstevel@tonic-gate 
18717c478bd9Sstevel@tonic-gate 	num_retry = nfs4_max_mount_retry;
18727c478bd9Sstevel@tonic-gate 
18737c478bd9Sstevel@tonic-gate 	do {
18747c478bd9Sstevel@tonic-gate 		/*
18757c478bd9Sstevel@tonic-gate 		 * Get the root fh from the server.  Retry nfs4_max_mount_retry
18767c478bd9Sstevel@tonic-gate 		 * (2) times if it fails with STALE since the recovery
18777c478bd9Sstevel@tonic-gate 		 * infrastructure doesn't do STALE recovery for components
18787c478bd9Sstevel@tonic-gate 		 * of the server path to the object being mounted.
18797c478bd9Sstevel@tonic-gate 		 */
18807c478bd9Sstevel@tonic-gate 		nfs4getfh_otw(mi, svp, &vtype, getfh_flags, CRED(), ep);
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate 		if (ep->error == 0 && ep->stat == NFS4_OK)
18837c478bd9Sstevel@tonic-gate 			break;
18847c478bd9Sstevel@tonic-gate 
18857c478bd9Sstevel@tonic-gate 		/*
18867c478bd9Sstevel@tonic-gate 		 * For some reason, the mount compound failed.  Before
18877c478bd9Sstevel@tonic-gate 		 * retrying, we need to restore the original sv_path
18887c478bd9Sstevel@tonic-gate 		 * because it might have contained symlinks that were
18897c478bd9Sstevel@tonic-gate 		 * expanded by nfsgetfh_otw before the failure occurred.
18907c478bd9Sstevel@tonic-gate 		 * replace current sv_path with orig sv_path -- just in case
18917c478bd9Sstevel@tonic-gate 		 * it changed due to embedded symlinks.
18927c478bd9Sstevel@tonic-gate 		 */
18937c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
18947c478bd9Sstevel@tonic-gate 		if (orig_sv_pathlen != svp->sv_pathlen) {
18957c478bd9Sstevel@tonic-gate 			kmem_free(svp->sv_path, svp->sv_pathlen);
18967c478bd9Sstevel@tonic-gate 			svp->sv_path = kmem_alloc(orig_sv_pathlen, KM_SLEEP);
18977c478bd9Sstevel@tonic-gate 			svp->sv_pathlen = orig_sv_pathlen;
18987c478bd9Sstevel@tonic-gate 		}
18997c478bd9Sstevel@tonic-gate 		bcopy(orig_sv_path, svp->sv_path, orig_sv_pathlen);
19007c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 	} while (num_retry-- > 0);
19037c478bd9Sstevel@tonic-gate 
19047c478bd9Sstevel@tonic-gate 	kmem_free(orig_sv_path, orig_sv_pathlen);
19057c478bd9Sstevel@tonic-gate 
19067c478bd9Sstevel@tonic-gate 	if (ep->error != 0 || ep->stat != 0) {
19077c478bd9Sstevel@tonic-gate 		return;
19087c478bd9Sstevel@tonic-gate 	}
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	if (vtype != VNON && vtype != mi->mi_type) {
19117c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
19127c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_WARN,
1913*b9238976Sth 		    "nfs4_remap_root: server root vnode type (%d) doesn't "
1914*b9238976Sth 		    "match mount info (%d)", vtype, mi->mi_type);
19157c478bd9Sstevel@tonic-gate 	}
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
19187c478bd9Sstevel@tonic-gate 	rootfh.nfs_fh4_val = svp->sv_fhandle.fh_buf;
19197c478bd9Sstevel@tonic-gate 	rootfh.nfs_fh4_len = svp->sv_fhandle.fh_len;
19207c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
19217c478bd9Sstevel@tonic-gate 	sfh4_update(mi->mi_rootfh, &rootfh);
19227c478bd9Sstevel@tonic-gate 
19237c478bd9Sstevel@tonic-gate 	/*
19248c9e5ad2Saalok 	 * It's possible that recovery took place on the filesystem
19258c9e5ad2Saalok 	 * and the server has been updated between the time we did
19268c9e5ad2Saalok 	 * the nfs4getfh_otw and now. Re-drive the otw operation
19278c9e5ad2Saalok 	 * to make sure we have a good fh.
19287c478bd9Sstevel@tonic-gate 	 */
19297c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
19308c9e5ad2Saalok 	if (mi->mi_curr_serv != svp)
19318c9e5ad2Saalok 		goto remap_retry;
19328c9e5ad2Saalok 
19337c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
19347c478bd9Sstevel@tonic-gate }
19357c478bd9Sstevel@tonic-gate 
19367c478bd9Sstevel@tonic-gate static int
19377c478bd9Sstevel@tonic-gate nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head,
1938*b9238976Sth     int flags, cred_t *cr, zone_t *zone)
19397c478bd9Sstevel@tonic-gate {
19407c478bd9Sstevel@tonic-gate 	vnode_t *rtvp = NULL;
19417c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
19427c478bd9Sstevel@tonic-gate 	dev_t nfs_dev;
19437c478bd9Sstevel@tonic-gate 	int error = 0;
19447c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
19457c478bd9Sstevel@tonic-gate 	int i;
19467c478bd9Sstevel@tonic-gate 	struct vattr va;
19477c478bd9Sstevel@tonic-gate 	vtype_t vtype = VNON;
19487c478bd9Sstevel@tonic-gate 	vtype_t tmp_vtype = VNON;
19497c478bd9Sstevel@tonic-gate 	struct servinfo4 *firstsvp = NULL, *svp = svp_head;
19507c478bd9Sstevel@tonic-gate 	nfs4_oo_hash_bucket_t *bucketp;
19517c478bd9Sstevel@tonic-gate 	nfs_fh4 fh;
19527c478bd9Sstevel@tonic-gate 	char *droptext = "";
19537c478bd9Sstevel@tonic-gate 	struct nfs_stats *nfsstatsp;
19547c478bd9Sstevel@tonic-gate 	nfs4_fname_t *mfname;
19557c478bd9Sstevel@tonic-gate 	nfs4_error_t e;
19567c478bd9Sstevel@tonic-gate 	char *orig_sv_path;
195750a83466Sjwahlig 	int orig_sv_pathlen, num_retry, removed;
19587c478bd9Sstevel@tonic-gate 	cred_t *lcr = NULL, *tcr = cr;
19597c478bd9Sstevel@tonic-gate 
1960108322fbScarlsonj 	nfsstatsp = zone_getspecific(nfsstat_zone_key, nfs_zone());
19617c478bd9Sstevel@tonic-gate 	ASSERT(nfsstatsp != NULL);
19627c478bd9Sstevel@tonic-gate 
1963108322fbScarlsonj 	ASSERT(nfs_zone() == zone);
19647c478bd9Sstevel@tonic-gate 	ASSERT(crgetref(cr));
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate 	/*
19677c478bd9Sstevel@tonic-gate 	 * Create a mount record and link it to the vfs struct.
19687c478bd9Sstevel@tonic-gate 	 */
19697c478bd9Sstevel@tonic-gate 	mi = kmem_zalloc(sizeof (*mi), KM_SLEEP);
19707c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_lock, NULL, MUTEX_DEFAULT, NULL);
19717c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_recovlock, NULL, RW_DEFAULT, NULL);
19727c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_rename_lock, NULL, RW_DEFAULT, NULL);
19737c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_fh_lock, NULL, RW_DEFAULT, NULL);
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate 	if (!(flags & NFSMNT_SOFT))
19767c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_HARD;
19777c478bd9Sstevel@tonic-gate 	if ((flags & NFSMNT_NOPRINT))
19787c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_NOPRINT;
19797c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_INT)
19807c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_INT;
19817c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_PUBLIC)
19827c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_PUBLIC;
1983*b9238976Sth 	if (flags & NFSMNT_MIRRORMOUNT)
1984*b9238976Sth 		mi->mi_flags |= MI4_MIRRORMOUNT;
19857c478bd9Sstevel@tonic-gate 	mi->mi_retrans = NFS_RETRIES;
19867c478bd9Sstevel@tonic-gate 	if (svp->sv_knconf->knc_semantics == NC_TPI_COTS_ORD ||
19877c478bd9Sstevel@tonic-gate 	    svp->sv_knconf->knc_semantics == NC_TPI_COTS)
19887c478bd9Sstevel@tonic-gate 		mi->mi_timeo = nfs4_cots_timeo;
19897c478bd9Sstevel@tonic-gate 	else
19907c478bd9Sstevel@tonic-gate 		mi->mi_timeo = NFS_TIMEO;
19917c478bd9Sstevel@tonic-gate 	mi->mi_prog = NFS_PROGRAM;
19927c478bd9Sstevel@tonic-gate 	mi->mi_vers = NFS_V4;
19937c478bd9Sstevel@tonic-gate 	mi->mi_rfsnames = rfsnames_v4;
19947c478bd9Sstevel@tonic-gate 	mi->mi_reqs = nfsstatsp->nfs_stats_v4.rfsreqcnt_ptr;
19957c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_failover_cv, NULL, CV_DEFAULT, NULL);
19967c478bd9Sstevel@tonic-gate 	mi->mi_servers = svp;
19977c478bd9Sstevel@tonic-gate 	mi->mi_curr_serv = svp;
19987c478bd9Sstevel@tonic-gate 	mi->mi_acregmin = SEC2HR(ACREGMIN);
19997c478bd9Sstevel@tonic-gate 	mi->mi_acregmax = SEC2HR(ACREGMAX);
20007c478bd9Sstevel@tonic-gate 	mi->mi_acdirmin = SEC2HR(ACDIRMIN);
20017c478bd9Sstevel@tonic-gate 	mi->mi_acdirmax = SEC2HR(ACDIRMAX);
20027c478bd9Sstevel@tonic-gate 	mi->mi_fh_expire_type = FH4_PERSISTENT;
20037c478bd9Sstevel@tonic-gate 	mi->mi_clientid_next = NULL;
20047c478bd9Sstevel@tonic-gate 	mi->mi_clientid_prev = NULL;
20057c478bd9Sstevel@tonic-gate 	mi->mi_grace_wait = 0;
20067c478bd9Sstevel@tonic-gate 	mi->mi_error = 0;
20077c478bd9Sstevel@tonic-gate 	mi->mi_srvsettime = 0;
20087c478bd9Sstevel@tonic-gate 
200950a83466Sjwahlig 	mi->mi_count = 1;
201050a83466Sjwahlig 
20117c478bd9Sstevel@tonic-gate 	mi->mi_tsize = nfs4_tsize(svp->sv_knconf);
20127c478bd9Sstevel@tonic-gate 	mi->mi_stsize = mi->mi_tsize;
20137c478bd9Sstevel@tonic-gate 
20147c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_DIRECTIO)
20157c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_DIRECTIO;
20167c478bd9Sstevel@tonic-gate 
20177c478bd9Sstevel@tonic-gate 	mi->mi_flags |= MI4_MOUNTING;
20187c478bd9Sstevel@tonic-gate 
20197c478bd9Sstevel@tonic-gate 	/*
20207c478bd9Sstevel@tonic-gate 	 * Make a vfs struct for nfs.  We do this here instead of below
20217c478bd9Sstevel@tonic-gate 	 * because rtvp needs a vfs before we can do a getattr on it.
20227c478bd9Sstevel@tonic-gate 	 *
20237c478bd9Sstevel@tonic-gate 	 * Assign a unique device id to the mount
20247c478bd9Sstevel@tonic-gate 	 */
20257c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs_minor_lock);
20267c478bd9Sstevel@tonic-gate 	do {
20277c478bd9Sstevel@tonic-gate 		nfs_minor = (nfs_minor + 1) & MAXMIN32;
20287c478bd9Sstevel@tonic-gate 		nfs_dev = makedevice(nfs_major, nfs_minor);
20297c478bd9Sstevel@tonic-gate 	} while (vfs_devismounted(nfs_dev));
20307c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs_minor_lock);
20317c478bd9Sstevel@tonic-gate 
20327c478bd9Sstevel@tonic-gate 	vfsp->vfs_dev = nfs_dev;
20337c478bd9Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, nfs_dev, nfs4fstyp);
20347c478bd9Sstevel@tonic-gate 	vfsp->vfs_data = (caddr_t)mi;
20357c478bd9Sstevel@tonic-gate 	vfsp->vfs_fstype = nfsfstyp;
20367c478bd9Sstevel@tonic-gate 	vfsp->vfs_bsize = nfs4_bsize;
20377c478bd9Sstevel@tonic-gate 
20387c478bd9Sstevel@tonic-gate 	/*
20397c478bd9Sstevel@tonic-gate 	 * Initialize fields used to support async putpage operations.
20407c478bd9Sstevel@tonic-gate 	 */
20417c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFS4_ASYNC_TYPES; i++)
20427c478bd9Sstevel@tonic-gate 		mi->mi_async_clusters[i] = nfs4_async_clusters;
20437c478bd9Sstevel@tonic-gate 	mi->mi_async_init_clusters = nfs4_async_clusters;
20447c478bd9Sstevel@tonic-gate 	mi->mi_async_curr = &mi->mi_async_reqs[0];
20457c478bd9Sstevel@tonic-gate 	mi->mi_max_threads = nfs4_max_threads;
20467c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_async_lock, NULL, MUTEX_DEFAULT, NULL);
20477c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_reqs_cv, NULL, CV_DEFAULT, NULL);
20487c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_work_cv, NULL, CV_DEFAULT, NULL);
20497c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_cv, NULL, CV_DEFAULT, NULL);
20507c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_inact_req_cv, NULL, CV_DEFAULT, NULL);
20517c478bd9Sstevel@tonic-gate 
20527c478bd9Sstevel@tonic-gate 	mi->mi_vfsp = vfsp;
20537c478bd9Sstevel@tonic-gate 	zone_hold(mi->mi_zone = zone);
20547c478bd9Sstevel@tonic-gate 	nfs4_mi_zonelist_add(mi);
20557c478bd9Sstevel@tonic-gate 
20567c478bd9Sstevel@tonic-gate 	/*
20577c478bd9Sstevel@tonic-gate 	 * Initialize the <open owner/cred> hash table.
20587c478bd9Sstevel@tonic-gate 	 */
20597c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFS4_NUM_OO_BUCKETS; i++) {
20607c478bd9Sstevel@tonic-gate 		bucketp = &(mi->mi_oo_list[i]);
20617c478bd9Sstevel@tonic-gate 		mutex_init(&bucketp->b_lock, NULL, MUTEX_DEFAULT, NULL);
20627c478bd9Sstevel@tonic-gate 		list_create(&bucketp->b_oo_hash_list,
20637c478bd9Sstevel@tonic-gate 		    sizeof (nfs4_open_owner_t),
20647c478bd9Sstevel@tonic-gate 		    offsetof(nfs4_open_owner_t, oo_hash_node));
20657c478bd9Sstevel@tonic-gate 	}
20667c478bd9Sstevel@tonic-gate 
20677c478bd9Sstevel@tonic-gate 	/*
20687c478bd9Sstevel@tonic-gate 	 * Initialize the freed open owner list.
20697c478bd9Sstevel@tonic-gate 	 */
20707c478bd9Sstevel@tonic-gate 	mi->mi_foo_num = 0;
20717c478bd9Sstevel@tonic-gate 	mi->mi_foo_max = NFS4_NUM_FREED_OPEN_OWNERS;
20727c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_foo_list, sizeof (nfs4_open_owner_t),
20737c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_open_owner_t, oo_foo_node));
20747c478bd9Sstevel@tonic-gate 
20757c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_lost_state, sizeof (nfs4_lost_rqst_t),
20767c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_lost_rqst_t, lr_node));
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_bseqid_list, sizeof (nfs4_bseqid_entry_t),
20797c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_bseqid_entry_t, bs_node));
20807c478bd9Sstevel@tonic-gate 
20817c478bd9Sstevel@tonic-gate 	/*
20827c478bd9Sstevel@tonic-gate 	 * Initialize the msg buffer.
20837c478bd9Sstevel@tonic-gate 	 */
20847c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_msg_list, sizeof (nfs4_debug_msg_t),
20857c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_debug_msg_t, msg_node));
20867c478bd9Sstevel@tonic-gate 	mi->mi_msg_count = 0;
20877c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_msg_list_lock, NULL, MUTEX_DEFAULT, NULL);
20887c478bd9Sstevel@tonic-gate 
20897c478bd9Sstevel@tonic-gate 	/*
20907c478bd9Sstevel@tonic-gate 	 * Initialize kstats
20917c478bd9Sstevel@tonic-gate 	 */
20927c478bd9Sstevel@tonic-gate 	nfs4_mnt_kstat_init(vfsp);
20937c478bd9Sstevel@tonic-gate 
20947c478bd9Sstevel@tonic-gate 	/*
20957c478bd9Sstevel@tonic-gate 	 * Initialize the shared filehandle pool, and get the fname for
20967c478bd9Sstevel@tonic-gate 	 * the filesystem root.
20977c478bd9Sstevel@tonic-gate 	 */
20987c478bd9Sstevel@tonic-gate 	sfh4_createtab(&mi->mi_filehandles);
20997c478bd9Sstevel@tonic-gate 	mi->mi_fname = fn_get(NULL, ".");
21007c478bd9Sstevel@tonic-gate 
21017c478bd9Sstevel@tonic-gate 	/*
21027c478bd9Sstevel@tonic-gate 	 * Save server path we're attempting to mount.
21037c478bd9Sstevel@tonic-gate 	 */
21047c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
21057c478bd9Sstevel@tonic-gate 	orig_sv_pathlen = svp_head->sv_pathlen;
21067c478bd9Sstevel@tonic-gate 	orig_sv_path = kmem_alloc(svp_head->sv_pathlen, KM_SLEEP);
21077c478bd9Sstevel@tonic-gate 	bcopy(svp_head->sv_path, orig_sv_path, svp_head->sv_pathlen);
21087c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
21097c478bd9Sstevel@tonic-gate 
21107c478bd9Sstevel@tonic-gate 	/*
21117c478bd9Sstevel@tonic-gate 	 * Make the GETFH call to get root fh for each replica.
21127c478bd9Sstevel@tonic-gate 	 */
21137c478bd9Sstevel@tonic-gate 	if (svp_head->sv_next)
21147c478bd9Sstevel@tonic-gate 		droptext = ", dropping replica";
21157c478bd9Sstevel@tonic-gate 
21167c478bd9Sstevel@tonic-gate 	/*
21177c478bd9Sstevel@tonic-gate 	 * If the uid is set then set the creds for secure mounts
21187c478bd9Sstevel@tonic-gate 	 * by proxy processes such as automountd.
21197c478bd9Sstevel@tonic-gate 	 */
21207c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
21217c478bd9Sstevel@tonic-gate 	if (svp->sv_secdata->uid != 0) {
21227c478bd9Sstevel@tonic-gate 		lcr = crdup(cr);
21237c478bd9Sstevel@tonic-gate 		(void) crsetugid(lcr, svp->sv_secdata->uid, crgetgid(cr));
21247c478bd9Sstevel@tonic-gate 		tcr = lcr;
21257c478bd9Sstevel@tonic-gate 	}
21267c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
21277c478bd9Sstevel@tonic-gate 	for (svp = svp_head; svp; svp = svp->sv_next) {
21287c478bd9Sstevel@tonic-gate 		if (nfs4_chkdup_servinfo4(svp_head, svp)) {
21297c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
2130*b9238976Sth 			    VERS_MSG "Host %s is a duplicate%s",
2131*b9238976Sth 			    svp->sv_hostname, droptext);
21327c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
21337c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
21347c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
21357c478bd9Sstevel@tonic-gate 			continue;
21367c478bd9Sstevel@tonic-gate 		}
21377c478bd9Sstevel@tonic-gate 		mi->mi_curr_serv = svp;
21387c478bd9Sstevel@tonic-gate 
21397c478bd9Sstevel@tonic-gate 		/*
21407c478bd9Sstevel@tonic-gate 		 * Just in case server path being mounted contains
21417c478bd9Sstevel@tonic-gate 		 * symlinks and fails w/STALE, save the initial sv_path
21427c478bd9Sstevel@tonic-gate 		 * so we can redrive the initial mount compound with the
21437c478bd9Sstevel@tonic-gate 		 * initial sv_path -- not a symlink-expanded version.
21447c478bd9Sstevel@tonic-gate 		 *
21457c478bd9Sstevel@tonic-gate 		 * This could only happen if a symlink was expanded
21467c478bd9Sstevel@tonic-gate 		 * and the expanded mount compound failed stale.  Because
21477c478bd9Sstevel@tonic-gate 		 * it could be the case that the symlink was removed at
21487c478bd9Sstevel@tonic-gate 		 * the server (and replaced with another symlink/dir,
21497c478bd9Sstevel@tonic-gate 		 * we need to use the initial sv_path when attempting
21507c478bd9Sstevel@tonic-gate 		 * to re-lookup everything and recover.
21517c478bd9Sstevel@tonic-gate 		 *
21527c478bd9Sstevel@tonic-gate 		 * Other mount errors should evenutally be handled here also
21537c478bd9Sstevel@tonic-gate 		 * (NFS4ERR_DELAY, NFS4ERR_RESOURCE).  For now, all mount
21547c478bd9Sstevel@tonic-gate 		 * failures will result in mount being redriven a few times.
21557c478bd9Sstevel@tonic-gate 		 */
21567c478bd9Sstevel@tonic-gate 		num_retry = nfs4_max_mount_retry;
21577c478bd9Sstevel@tonic-gate 		do {
21587c478bd9Sstevel@tonic-gate 			nfs4getfh_otw(mi, svp, &tmp_vtype,
21597c478bd9Sstevel@tonic-gate 			    ((flags & NFSMNT_PUBLIC) ? NFS4_GETFH_PUBLIC : 0) |
21607c478bd9Sstevel@tonic-gate 			    NFS4_GETFH_NEEDSOP, tcr, &e);
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate 			if (e.error == 0 && e.stat == NFS4_OK)
21637c478bd9Sstevel@tonic-gate 				break;
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate 			/*
21667c478bd9Sstevel@tonic-gate 			 * replace current sv_path with orig sv_path -- just in
21677c478bd9Sstevel@tonic-gate 			 * case it changed due to embedded symlinks.
21687c478bd9Sstevel@tonic-gate 			 */
21697c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
21707c478bd9Sstevel@tonic-gate 			if (orig_sv_pathlen != svp->sv_pathlen) {
21717c478bd9Sstevel@tonic-gate 				kmem_free(svp->sv_path, svp->sv_pathlen);
21727c478bd9Sstevel@tonic-gate 				svp->sv_path = kmem_alloc(orig_sv_pathlen,
2173*b9238976Sth 				    KM_SLEEP);
21747c478bd9Sstevel@tonic-gate 				svp->sv_pathlen = orig_sv_pathlen;
21757c478bd9Sstevel@tonic-gate 			}
21767c478bd9Sstevel@tonic-gate 			bcopy(orig_sv_path, svp->sv_path, orig_sv_pathlen);
21777c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
21787c478bd9Sstevel@tonic-gate 
21797c478bd9Sstevel@tonic-gate 		} while (num_retry-- > 0);
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate 		error = e.error ? e.error : geterrno4(e.stat);
21827c478bd9Sstevel@tonic-gate 		if (error) {
21837c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
2184*b9238976Sth 			    VERS_MSG "initial call to %s failed%s: %m",
2185*b9238976Sth 			    svp->sv_hostname, droptext);
21867c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
21877c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
21887c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
21897c478bd9Sstevel@tonic-gate 			mi->mi_flags &= ~MI4_RECOV_FAIL;
21907c478bd9Sstevel@tonic-gate 			mi->mi_error = 0;
21917c478bd9Sstevel@tonic-gate 			continue;
21927c478bd9Sstevel@tonic-gate 		}
21937c478bd9Sstevel@tonic-gate 
21947c478bd9Sstevel@tonic-gate 		if (tmp_vtype == VBAD) {
21957c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_WARN,
2196*b9238976Sth 			    VERS_MSG "%s returned a bad file type for "
2197*b9238976Sth 			    "root%s", svp->sv_hostname, droptext);
21987c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
21997c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
22007c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
22017c478bd9Sstevel@tonic-gate 			continue;
22027c478bd9Sstevel@tonic-gate 		}
22037c478bd9Sstevel@tonic-gate 
22047c478bd9Sstevel@tonic-gate 		if (vtype == VNON) {
22057c478bd9Sstevel@tonic-gate 			vtype = tmp_vtype;
22067c478bd9Sstevel@tonic-gate 		} else if (vtype != tmp_vtype) {
22077c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_WARN,
2208*b9238976Sth 			    VERS_MSG "%s returned a different file type "
2209*b9238976Sth 			    "for root%s", svp->sv_hostname, droptext);
22107c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
22117c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
22127c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
22137c478bd9Sstevel@tonic-gate 			continue;
22147c478bd9Sstevel@tonic-gate 		}
22157c478bd9Sstevel@tonic-gate 		if (firstsvp == NULL)
22167c478bd9Sstevel@tonic-gate 			firstsvp = svp;
22177c478bd9Sstevel@tonic-gate 	}
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate 	kmem_free(orig_sv_path, orig_sv_pathlen);
22207c478bd9Sstevel@tonic-gate 
22217c478bd9Sstevel@tonic-gate 	if (firstsvp == NULL) {
22227c478bd9Sstevel@tonic-gate 		if (error == 0)
22237c478bd9Sstevel@tonic-gate 			error = ENOENT;
22247c478bd9Sstevel@tonic-gate 		goto bad;
22257c478bd9Sstevel@tonic-gate 	}
22267c478bd9Sstevel@tonic-gate 
22277c478bd9Sstevel@tonic-gate 	mi->mi_curr_serv = svp = firstsvp;
22287c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
22297c478bd9Sstevel@tonic-gate 	ASSERT((mi->mi_curr_serv->sv_flags & SV4_NOTINUSE) == 0);
22307c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_len = svp->sv_fhandle.fh_len;
22317c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_val = svp->sv_fhandle.fh_buf;
22327c478bd9Sstevel@tonic-gate 	mi->mi_rootfh = sfh4_get(&fh, mi);
22337c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_len = svp->sv_pfhandle.fh_len;
22347c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_val = svp->sv_pfhandle.fh_buf;
22357c478bd9Sstevel@tonic-gate 	mi->mi_srvparentfh = sfh4_get(&fh, mi);
22367c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
22377c478bd9Sstevel@tonic-gate 
22387c478bd9Sstevel@tonic-gate 	/*
22397c478bd9Sstevel@tonic-gate 	 * Make the root vnode without attributes.
22407c478bd9Sstevel@tonic-gate 	 */
22417c478bd9Sstevel@tonic-gate 	mfname = mi->mi_fname;
22427c478bd9Sstevel@tonic-gate 	fn_hold(mfname);
22437c478bd9Sstevel@tonic-gate 	rtvp = makenfs4node_by_fh(mi->mi_rootfh, NULL,
22447c478bd9Sstevel@tonic-gate 	    &mfname, NULL, mi, cr, gethrtime());
22457c478bd9Sstevel@tonic-gate 	rtvp->v_type = vtype;
22467c478bd9Sstevel@tonic-gate 
22477c478bd9Sstevel@tonic-gate 	mi->mi_curread = mi->mi_tsize;
22487c478bd9Sstevel@tonic-gate 	mi->mi_curwrite = mi->mi_stsize;
22497c478bd9Sstevel@tonic-gate 
22507c478bd9Sstevel@tonic-gate 	/*
22517c478bd9Sstevel@tonic-gate 	 * Start the manager thread responsible for handling async worker
22527c478bd9Sstevel@tonic-gate 	 * threads.
22537c478bd9Sstevel@tonic-gate 	 */
225450a83466Sjwahlig 	MI4_HOLD(mi);
22557c478bd9Sstevel@tonic-gate 	VFS_HOLD(vfsp);	/* add reference for thread */
22567c478bd9Sstevel@tonic-gate 	mi->mi_manager_thread = zthread_create(NULL, 0, nfs4_async_manager,
2257*b9238976Sth 	    vfsp, 0, minclsyspri);
22587c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_manager_thread != NULL);
225950a83466Sjwahlig 
22607c478bd9Sstevel@tonic-gate 	/*
22617c478bd9Sstevel@tonic-gate 	 * Create the thread that handles over-the-wire calls for
22627c478bd9Sstevel@tonic-gate 	 * VOP_INACTIVE.
22637c478bd9Sstevel@tonic-gate 	 * This needs to happen after the manager thread is created.
22647c478bd9Sstevel@tonic-gate 	 */
226550a83466Sjwahlig 	MI4_HOLD(mi);
22667c478bd9Sstevel@tonic-gate 	mi->mi_inactive_thread = zthread_create(NULL, 0, nfs4_inactive_thread,
2267*b9238976Sth 	    mi, 0, minclsyspri);
22687c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_inactive_thread != NULL);
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate 	/* If we didn't get a type, get one now */
22717c478bd9Sstevel@tonic-gate 	if (rtvp->v_type == VNON) {
22727c478bd9Sstevel@tonic-gate 		va.va_mask = AT_TYPE;
22737c478bd9Sstevel@tonic-gate 		error = nfs4getattr(rtvp, &va, tcr);
22747c478bd9Sstevel@tonic-gate 		if (error)
22757c478bd9Sstevel@tonic-gate 			goto bad;
22767c478bd9Sstevel@tonic-gate 		rtvp->v_type = va.va_type;
22777c478bd9Sstevel@tonic-gate 	}
22787c478bd9Sstevel@tonic-gate 
22797c478bd9Sstevel@tonic-gate 	mi->mi_type = rtvp->v_type;
22807c478bd9Sstevel@tonic-gate 
22817c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
22827c478bd9Sstevel@tonic-gate 	mi->mi_flags &= ~MI4_MOUNTING;
22837c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
22847c478bd9Sstevel@tonic-gate 
22857c478bd9Sstevel@tonic-gate 	*rtvpp = rtvp;
22867c478bd9Sstevel@tonic-gate 	if (lcr != NULL)
22877c478bd9Sstevel@tonic-gate 		crfree(lcr);
22887c478bd9Sstevel@tonic-gate 
22897c478bd9Sstevel@tonic-gate 	return (0);
22907c478bd9Sstevel@tonic-gate bad:
22917c478bd9Sstevel@tonic-gate 	/*
22927c478bd9Sstevel@tonic-gate 	 * An error occurred somewhere, need to clean up...
22937c478bd9Sstevel@tonic-gate 	 */
22947c478bd9Sstevel@tonic-gate 	if (lcr != NULL)
22957c478bd9Sstevel@tonic-gate 		crfree(lcr);
2296*b9238976Sth 
22977c478bd9Sstevel@tonic-gate 	if (rtvp != NULL) {
22987c478bd9Sstevel@tonic-gate 		/*
22997c478bd9Sstevel@tonic-gate 		 * We need to release our reference to the root vnode and
23007c478bd9Sstevel@tonic-gate 		 * destroy the mntinfo4 struct that we just created.
23017c478bd9Sstevel@tonic-gate 		 */
23027c478bd9Sstevel@tonic-gate 		rp = VTOR4(rtvp);
23037c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R4HASHED)
23047c478bd9Sstevel@tonic-gate 			rp4_rmhash(rp);
23057c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
23067c478bd9Sstevel@tonic-gate 	}
23077c478bd9Sstevel@tonic-gate 	nfs4_async_stop(vfsp);
23087c478bd9Sstevel@tonic-gate 	nfs4_async_manager_stop(vfsp);
230950a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
231050a83466Sjwahlig 	if (removed)
231150a83466Sjwahlig 		zone_rele(mi->mi_zone);
231250a83466Sjwahlig 
231350a83466Sjwahlig 	/*
231450a83466Sjwahlig 	 * This releases the initial "hold" of the mi since it will never
231550a83466Sjwahlig 	 * be referenced by the vfsp.  Also, when mount returns to vfs.c
231650a83466Sjwahlig 	 * with an error, the vfsp will be destroyed, not rele'd.
231750a83466Sjwahlig 	 */
231850a83466Sjwahlig 	MI4_RELE(mi);
231950a83466Sjwahlig 
23207c478bd9Sstevel@tonic-gate 	*rtvpp = NULL;
23217c478bd9Sstevel@tonic-gate 	return (error);
23227c478bd9Sstevel@tonic-gate }
23237c478bd9Sstevel@tonic-gate 
23247c478bd9Sstevel@tonic-gate /*
23257c478bd9Sstevel@tonic-gate  * vfs operations
23267c478bd9Sstevel@tonic-gate  */
23277c478bd9Sstevel@tonic-gate static int
23287c478bd9Sstevel@tonic-gate nfs4_unmount(vfs_t *vfsp, int flag, cred_t *cr)
23297c478bd9Sstevel@tonic-gate {
2330*b9238976Sth 	mntinfo4_t		*mi;
2331*b9238976Sth 	ushort_t		omax;
2332*b9238976Sth 	int			removed;
2333*b9238976Sth 
2334*b9238976Sth 	bool_t			must_unlock = FALSE;
2335*b9238976Sth 
2336*b9238976Sth 	nfs4_ephemeral_tree_t	*eph_tree;
23377c478bd9Sstevel@tonic-gate 
23387c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
23397c478bd9Sstevel@tonic-gate 		return (EPERM);
23407c478bd9Sstevel@tonic-gate 
23417c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
23427c478bd9Sstevel@tonic-gate 
23437c478bd9Sstevel@tonic-gate 	if (flag & MS_FORCE) {
23447c478bd9Sstevel@tonic-gate 		vfsp->vfs_flag |= VFS_UNMOUNTED;
2345108322fbScarlsonj 		if (nfs_zone() != mi->mi_zone) {
23467c478bd9Sstevel@tonic-gate 			/*
23477c478bd9Sstevel@tonic-gate 			 * If the request is coming from the wrong zone,
23487c478bd9Sstevel@tonic-gate 			 * we don't want to create any new threads, and
23497c478bd9Sstevel@tonic-gate 			 * performance is not a concern.  Do everything
23507c478bd9Sstevel@tonic-gate 			 * inline.
23517c478bd9Sstevel@tonic-gate 			 */
23527c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_zone_debug, (CE_NOTE,
23537c478bd9Sstevel@tonic-gate 			    "nfs4_unmount x-zone forced unmount of vfs %p\n",
23547c478bd9Sstevel@tonic-gate 			    (void *)vfsp));
2355*b9238976Sth 			nfs4_free_mount(vfsp, flag, cr);
23567c478bd9Sstevel@tonic-gate 		} else {
23577c478bd9Sstevel@tonic-gate 			/*
23587c478bd9Sstevel@tonic-gate 			 * Free data structures asynchronously, to avoid
23597c478bd9Sstevel@tonic-gate 			 * blocking the current thread (for performance
23607c478bd9Sstevel@tonic-gate 			 * reasons only).
23617c478bd9Sstevel@tonic-gate 			 */
2362*b9238976Sth 			async_free_mount(vfsp, flag, cr);
23637c478bd9Sstevel@tonic-gate 		}
2364*b9238976Sth 
23657c478bd9Sstevel@tonic-gate 		return (0);
23667c478bd9Sstevel@tonic-gate 	}
2367*b9238976Sth 
23687c478bd9Sstevel@tonic-gate 	/*
23697c478bd9Sstevel@tonic-gate 	 * Wait until all asynchronous putpage operations on
23707c478bd9Sstevel@tonic-gate 	 * this file system are complete before flushing rnodes
23717c478bd9Sstevel@tonic-gate 	 * from the cache.
23727c478bd9Sstevel@tonic-gate 	 */
23737c478bd9Sstevel@tonic-gate 	omax = mi->mi_max_threads;
2374*b9238976Sth 	if (nfs4_async_stop_sig(vfsp))
23757c478bd9Sstevel@tonic-gate 		return (EINTR);
2376*b9238976Sth 
23777c478bd9Sstevel@tonic-gate 	r4flush(vfsp, cr);
2378*b9238976Sth 
2379*b9238976Sth 	(void) nfs4_ephemeral_umount(mi, flag, cr,
2380*b9238976Sth 	    &must_unlock, &eph_tree);
2381*b9238976Sth 
23827c478bd9Sstevel@tonic-gate 	/*
23837c478bd9Sstevel@tonic-gate 	 * If there are any active vnodes on this file system,
2384*b9238976Sth 	 * then the file system is busy and can't be unmounted.
23857c478bd9Sstevel@tonic-gate 	 */
23867c478bd9Sstevel@tonic-gate 	if (check_rtable4(vfsp)) {
2387*b9238976Sth 		nfs4_ephemeral_umount_unlock(&must_unlock, &eph_tree);
2388*b9238976Sth 
23897c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_async_lock);
23907c478bd9Sstevel@tonic-gate 		mi->mi_max_threads = omax;
23917c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_async_lock);
2392*b9238976Sth 
23937c478bd9Sstevel@tonic-gate 		return (EBUSY);
23947c478bd9Sstevel@tonic-gate 	}
2395*b9238976Sth 
2396*b9238976Sth 	/*
2397*b9238976Sth 	 * The unmount can't fail from now on, so record any
2398*b9238976Sth 	 * ephemeral changes.
2399*b9238976Sth 	 */
2400*b9238976Sth 	nfs4_ephemeral_umount_activate(mi, &must_unlock, &eph_tree);
2401*b9238976Sth 
24027c478bd9Sstevel@tonic-gate 	/*
2403*b9238976Sth 	 * There are no active files that could require over-the-wire
2404*b9238976Sth 	 * calls to the server, so stop the async manager and the
2405*b9238976Sth 	 * inactive thread.
24067c478bd9Sstevel@tonic-gate 	 */
24077c478bd9Sstevel@tonic-gate 	nfs4_async_manager_stop(vfsp);
2408*b9238976Sth 
24097c478bd9Sstevel@tonic-gate 	/*
24107c478bd9Sstevel@tonic-gate 	 * Destroy all rnodes belonging to this file system from the
24117c478bd9Sstevel@tonic-gate 	 * rnode hash queues and purge any resources allocated to
24127c478bd9Sstevel@tonic-gate 	 * them.
24137c478bd9Sstevel@tonic-gate 	 */
24147c478bd9Sstevel@tonic-gate 	destroy_rtable4(vfsp, cr);
24157c478bd9Sstevel@tonic-gate 	vfsp->vfs_flag |= VFS_UNMOUNTED;
241650a83466Sjwahlig 
24177c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server(mi, NULL);
241850a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
241950a83466Sjwahlig 	if (removed)
242050a83466Sjwahlig 		zone_rele(mi->mi_zone);
242150a83466Sjwahlig 
24227c478bd9Sstevel@tonic-gate 	return (0);
24237c478bd9Sstevel@tonic-gate }
24247c478bd9Sstevel@tonic-gate 
24257c478bd9Sstevel@tonic-gate /*
24267c478bd9Sstevel@tonic-gate  * find root of nfs
24277c478bd9Sstevel@tonic-gate  */
24287c478bd9Sstevel@tonic-gate static int
24297c478bd9Sstevel@tonic-gate nfs4_root(vfs_t *vfsp, vnode_t **vpp)
24307c478bd9Sstevel@tonic-gate {
24317c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
24327c478bd9Sstevel@tonic-gate 	vnode_t *vp;
24337c478bd9Sstevel@tonic-gate 	nfs4_fname_t *mfname;
24347c478bd9Sstevel@tonic-gate 	servinfo4_t *svp;
24357c478bd9Sstevel@tonic-gate 
24367c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
24377c478bd9Sstevel@tonic-gate 
2438108322fbScarlsonj 	if (nfs_zone() != mi->mi_zone)
24397c478bd9Sstevel@tonic-gate 		return (EPERM);
24407c478bd9Sstevel@tonic-gate 
24417c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
24427c478bd9Sstevel@tonic-gate 	if (svp) {
24437c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
24447c478bd9Sstevel@tonic-gate 		if (svp->sv_flags & SV4_ROOT_STALE) {
24457c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
24467c478bd9Sstevel@tonic-gate 
24477c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
24487c478bd9Sstevel@tonic-gate 			if (svp->sv_flags & SV4_ROOT_STALE) {
24497c478bd9Sstevel@tonic-gate 				svp->sv_flags &= ~SV4_ROOT_STALE;
24507c478bd9Sstevel@tonic-gate 				nfs_rw_exit(&svp->sv_lock);
24517c478bd9Sstevel@tonic-gate 				return (ENOENT);
24527c478bd9Sstevel@tonic-gate 			}
24537c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
24547c478bd9Sstevel@tonic-gate 		} else
24557c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
24567c478bd9Sstevel@tonic-gate 	}
24577c478bd9Sstevel@tonic-gate 
24587c478bd9Sstevel@tonic-gate 	mfname = mi->mi_fname;
24597c478bd9Sstevel@tonic-gate 	fn_hold(mfname);
24607c478bd9Sstevel@tonic-gate 	vp = makenfs4node_by_fh(mi->mi_rootfh, NULL, &mfname, NULL,
24617c478bd9Sstevel@tonic-gate 	    VFTOMI4(vfsp), CRED(), gethrtime());
24627c478bd9Sstevel@tonic-gate 
24637c478bd9Sstevel@tonic-gate 	if (VTOR4(vp)->r_flags & R4STALE) {
24647c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
24657c478bd9Sstevel@tonic-gate 		return (ENOENT);
24667c478bd9Sstevel@tonic-gate 	}
24677c478bd9Sstevel@tonic-gate 
24687c478bd9Sstevel@tonic-gate 	ASSERT(vp->v_type == VNON || vp->v_type == mi->mi_type);
24697c478bd9Sstevel@tonic-gate 
24707c478bd9Sstevel@tonic-gate 	vp->v_type = mi->mi_type;
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate 	*vpp = vp;
24737c478bd9Sstevel@tonic-gate 
24747c478bd9Sstevel@tonic-gate 	return (0);
24757c478bd9Sstevel@tonic-gate }
24767c478bd9Sstevel@tonic-gate 
24777c478bd9Sstevel@tonic-gate static int
24787c478bd9Sstevel@tonic-gate nfs4_statfs_otw(vnode_t *vp, struct statvfs64 *sbp, cred_t *cr)
24797c478bd9Sstevel@tonic-gate {
24807c478bd9Sstevel@tonic-gate 	int error;
24817c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t gar;
24827c478bd9Sstevel@tonic-gate 	nfs4_ga_ext_res_t ger;
24837c478bd9Sstevel@tonic-gate 
24847c478bd9Sstevel@tonic-gate 	gar.n4g_ext_res = &ger;
24857c478bd9Sstevel@tonic-gate 
24867c478bd9Sstevel@tonic-gate 	if (error = nfs4_attr_otw(vp, TAG_FSINFO, &gar,
24877c478bd9Sstevel@tonic-gate 	    NFS4_STATFS_ATTR_MASK, cr))
24887c478bd9Sstevel@tonic-gate 		return (error);
24897c478bd9Sstevel@tonic-gate 
24907c478bd9Sstevel@tonic-gate 	*sbp = gar.n4g_ext_res->n4g_sb;
24917c478bd9Sstevel@tonic-gate 
24927c478bd9Sstevel@tonic-gate 	return (0);
24937c478bd9Sstevel@tonic-gate }
24947c478bd9Sstevel@tonic-gate 
24957c478bd9Sstevel@tonic-gate /*
24967c478bd9Sstevel@tonic-gate  * Get file system statistics.
24977c478bd9Sstevel@tonic-gate  */
24987c478bd9Sstevel@tonic-gate static int
24997c478bd9Sstevel@tonic-gate nfs4_statvfs(vfs_t *vfsp, struct statvfs64 *sbp)
25007c478bd9Sstevel@tonic-gate {
25017c478bd9Sstevel@tonic-gate 	int error;
25027c478bd9Sstevel@tonic-gate 	vnode_t *vp;
25037c478bd9Sstevel@tonic-gate 	cred_t *cr;
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 	error = nfs4_root(vfsp, &vp);
25067c478bd9Sstevel@tonic-gate 	if (error)
25077c478bd9Sstevel@tonic-gate 		return (error);
25087c478bd9Sstevel@tonic-gate 
25097c478bd9Sstevel@tonic-gate 	cr = CRED();
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 	error = nfs4_statfs_otw(vp, sbp, cr);
25127c478bd9Sstevel@tonic-gate 	if (!error) {
25137c478bd9Sstevel@tonic-gate 		(void) strncpy(sbp->f_basetype,
2514*b9238976Sth 		    vfssw[vfsp->vfs_fstype].vsw_name, FSTYPSZ);
25157c478bd9Sstevel@tonic-gate 		sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
25167c478bd9Sstevel@tonic-gate 	} else {
25177c478bd9Sstevel@tonic-gate 		nfs4_purge_stale_fh(error, vp, cr);
25187c478bd9Sstevel@tonic-gate 	}
25197c478bd9Sstevel@tonic-gate 
25207c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
25217c478bd9Sstevel@tonic-gate 
25227c478bd9Sstevel@tonic-gate 	return (error);
25237c478bd9Sstevel@tonic-gate }
25247c478bd9Sstevel@tonic-gate 
25257c478bd9Sstevel@tonic-gate static kmutex_t nfs4_syncbusy;
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate /*
25287c478bd9Sstevel@tonic-gate  * Flush dirty nfs files for file system vfsp.
25297c478bd9Sstevel@tonic-gate  * If vfsp == NULL, all nfs files are flushed.
25307c478bd9Sstevel@tonic-gate  *
25317c478bd9Sstevel@tonic-gate  * SYNC_CLOSE in flag is passed to us to
25327c478bd9Sstevel@tonic-gate  * indicate that we are shutting down and or
25337c478bd9Sstevel@tonic-gate  * rebooting.
25347c478bd9Sstevel@tonic-gate  */
25357c478bd9Sstevel@tonic-gate static int
25367c478bd9Sstevel@tonic-gate nfs4_sync(vfs_t *vfsp, short flag, cred_t *cr)
25377c478bd9Sstevel@tonic-gate {
25387c478bd9Sstevel@tonic-gate 	/*
25397c478bd9Sstevel@tonic-gate 	 * Cross-zone calls are OK here, since this translates to a
25407c478bd9Sstevel@tonic-gate 	 * VOP_PUTPAGE(B_ASYNC), which gets picked up by the right zone.
25417c478bd9Sstevel@tonic-gate 	 */
25427c478bd9Sstevel@tonic-gate 	if (!(flag & SYNC_ATTR) && mutex_tryenter(&nfs4_syncbusy) != 0) {
25437c478bd9Sstevel@tonic-gate 		r4flush(vfsp, cr);
25447c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_syncbusy);
25457c478bd9Sstevel@tonic-gate 	}
25467c478bd9Sstevel@tonic-gate 
25477c478bd9Sstevel@tonic-gate 	/*
25487c478bd9Sstevel@tonic-gate 	 * if SYNC_CLOSE is set then we know that
25497c478bd9Sstevel@tonic-gate 	 * the system is rebooting, mark the mntinfo
25507c478bd9Sstevel@tonic-gate 	 * for later examination.
25517c478bd9Sstevel@tonic-gate 	 */
25527c478bd9Sstevel@tonic-gate 	if (vfsp && (flag & SYNC_CLOSE)) {
25537c478bd9Sstevel@tonic-gate 		mntinfo4_t *mi;
25547c478bd9Sstevel@tonic-gate 
25557c478bd9Sstevel@tonic-gate 		mi = VFTOMI4(vfsp);
25567c478bd9Sstevel@tonic-gate 		if (!(mi->mi_flags & MI4_SHUTDOWN)) {
25577c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
25587c478bd9Sstevel@tonic-gate 			mi->mi_flags |= MI4_SHUTDOWN;
25597c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
25607c478bd9Sstevel@tonic-gate 		}
25617c478bd9Sstevel@tonic-gate 	}
25627c478bd9Sstevel@tonic-gate 	return (0);
25637c478bd9Sstevel@tonic-gate }
25647c478bd9Sstevel@tonic-gate 
25657c478bd9Sstevel@tonic-gate /*
25667c478bd9Sstevel@tonic-gate  * vget is difficult, if not impossible, to support in v4 because we don't
25677c478bd9Sstevel@tonic-gate  * know the parent directory or name, which makes it impossible to create a
25687c478bd9Sstevel@tonic-gate  * useful shadow vnode.  And we need the shadow vnode for things like
25697c478bd9Sstevel@tonic-gate  * OPEN.
25707c478bd9Sstevel@tonic-gate  */
25717c478bd9Sstevel@tonic-gate 
25727c478bd9Sstevel@tonic-gate /* ARGSUSED */
25737c478bd9Sstevel@tonic-gate /*
25747c478bd9Sstevel@tonic-gate  * XXX Check nfs4_vget_pseudo() for dependency.
25757c478bd9Sstevel@tonic-gate  */
25767c478bd9Sstevel@tonic-gate static int
25777c478bd9Sstevel@tonic-gate nfs4_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
25787c478bd9Sstevel@tonic-gate {
25797c478bd9Sstevel@tonic-gate 	return (EREMOTE);
25807c478bd9Sstevel@tonic-gate }
25817c478bd9Sstevel@tonic-gate 
25827c478bd9Sstevel@tonic-gate /*
25837c478bd9Sstevel@tonic-gate  * nfs4_mountroot get called in the case where we are diskless booting.  All
25847c478bd9Sstevel@tonic-gate  * we need from here is the ability to get the server info and from there we
25857c478bd9Sstevel@tonic-gate  * can simply call nfs4_rootvp.
25867c478bd9Sstevel@tonic-gate  */
25877c478bd9Sstevel@tonic-gate /* ARGSUSED */
25887c478bd9Sstevel@tonic-gate static int
25897c478bd9Sstevel@tonic-gate nfs4_mountroot(vfs_t *vfsp, whymountroot_t why)
25907c478bd9Sstevel@tonic-gate {
25917c478bd9Sstevel@tonic-gate 	vnode_t *rtvp;
25927c478bd9Sstevel@tonic-gate 	char root_hostname[SYS_NMLN+1];
25937c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp;
25947c478bd9Sstevel@tonic-gate 	int error;
25957c478bd9Sstevel@tonic-gate 	int vfsflags;
25967c478bd9Sstevel@tonic-gate 	size_t size;
25977c478bd9Sstevel@tonic-gate 	char *root_path;
25987c478bd9Sstevel@tonic-gate 	struct pathname pn;
25997c478bd9Sstevel@tonic-gate 	char *name;
26007c478bd9Sstevel@tonic-gate 	cred_t *cr;
26017c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
26027c478bd9Sstevel@tonic-gate 	struct nfs_args args;		/* nfs mount arguments */
26037c478bd9Sstevel@tonic-gate 	static char token[10];
26047c478bd9Sstevel@tonic-gate 	nfs4_error_t n4e;
26057c478bd9Sstevel@tonic-gate 
26067c478bd9Sstevel@tonic-gate 	bzero(&args, sizeof (args));
26077c478bd9Sstevel@tonic-gate 
26087c478bd9Sstevel@tonic-gate 	/* do this BEFORE getfile which causes xid stamps to be initialized */
26097c478bd9Sstevel@tonic-gate 	clkset(-1L);		/* hack for now - until we get time svc? */
26107c478bd9Sstevel@tonic-gate 
26117c478bd9Sstevel@tonic-gate 	if (why == ROOT_REMOUNT) {
26127c478bd9Sstevel@tonic-gate 		/*
26137c478bd9Sstevel@tonic-gate 		 * Shouldn't happen.
26147c478bd9Sstevel@tonic-gate 		 */
26157c478bd9Sstevel@tonic-gate 		panic("nfs4_mountroot: why == ROOT_REMOUNT");
26167c478bd9Sstevel@tonic-gate 	}
26177c478bd9Sstevel@tonic-gate 
26187c478bd9Sstevel@tonic-gate 	if (why == ROOT_UNMOUNT) {
26197c478bd9Sstevel@tonic-gate 		/*
26207c478bd9Sstevel@tonic-gate 		 * Nothing to do for NFS.
26217c478bd9Sstevel@tonic-gate 		 */
26227c478bd9Sstevel@tonic-gate 		return (0);
26237c478bd9Sstevel@tonic-gate 	}
26247c478bd9Sstevel@tonic-gate 
26257c478bd9Sstevel@tonic-gate 	/*
26267c478bd9Sstevel@tonic-gate 	 * why == ROOT_INIT
26277c478bd9Sstevel@tonic-gate 	 */
26287c478bd9Sstevel@tonic-gate 
26297c478bd9Sstevel@tonic-gate 	name = token;
26307c478bd9Sstevel@tonic-gate 	*name = 0;
26317c478bd9Sstevel@tonic-gate 	(void) getfsname("root", name, sizeof (token));
26327c478bd9Sstevel@tonic-gate 
26337c478bd9Sstevel@tonic-gate 	pn_alloc(&pn);
26347c478bd9Sstevel@tonic-gate 	root_path = pn.pn_path;
26357c478bd9Sstevel@tonic-gate 
26367c478bd9Sstevel@tonic-gate 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
26377c478bd9Sstevel@tonic-gate 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
26387c478bd9Sstevel@tonic-gate 	svp->sv_knconf = kmem_zalloc(sizeof (*svp->sv_knconf), KM_SLEEP);
26397c478bd9Sstevel@tonic-gate 	svp->sv_knconf->knc_protofmly = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
26407c478bd9Sstevel@tonic-gate 	svp->sv_knconf->knc_proto = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
26417c478bd9Sstevel@tonic-gate 
26427c478bd9Sstevel@tonic-gate 	/*
26437c478bd9Sstevel@tonic-gate 	 * Get server address
26447c478bd9Sstevel@tonic-gate 	 * Get the root path
26457c478bd9Sstevel@tonic-gate 	 * Get server's transport
26467c478bd9Sstevel@tonic-gate 	 * Get server's hostname
26477c478bd9Sstevel@tonic-gate 	 * Get options
26487c478bd9Sstevel@tonic-gate 	 */
26497c478bd9Sstevel@tonic-gate 	args.addr = &svp->sv_addr;
26507c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
26517c478bd9Sstevel@tonic-gate 	args.fh = (char *)&svp->sv_fhandle;
26527c478bd9Sstevel@tonic-gate 	args.knconf = svp->sv_knconf;
26537c478bd9Sstevel@tonic-gate 	args.hostname = root_hostname;
26547c478bd9Sstevel@tonic-gate 	vfsflags = 0;
26557c478bd9Sstevel@tonic-gate 	if (error = mount_root(*name ? name : "root", root_path, NFS_V4,
26567c478bd9Sstevel@tonic-gate 	    &args, &vfsflags)) {
26577c478bd9Sstevel@tonic-gate 		if (error == EPROTONOSUPPORT)
26587c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN, "nfs4_mountroot: "
26597c478bd9Sstevel@tonic-gate 			    "mount_root failed: server doesn't support NFS V4");
26607c478bd9Sstevel@tonic-gate 		else
26617c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
26627c478bd9Sstevel@tonic-gate 			    "nfs4_mountroot: mount_root failed: %m");
26637c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
26647c478bd9Sstevel@tonic-gate 		sv4_free(svp);
26657c478bd9Sstevel@tonic-gate 		pn_free(&pn);
26667c478bd9Sstevel@tonic-gate 		return (error);
26677c478bd9Sstevel@tonic-gate 	}
26687c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
26697c478bd9Sstevel@tonic-gate 	svp->sv_hostnamelen = (int)(strlen(root_hostname) + 1);
26707c478bd9Sstevel@tonic-gate 	svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP);
26717c478bd9Sstevel@tonic-gate 	(void) strcpy(svp->sv_hostname, root_hostname);
26727c478bd9Sstevel@tonic-gate 
26737c478bd9Sstevel@tonic-gate 	svp->sv_pathlen = (int)(strlen(root_path) + 1);
26747c478bd9Sstevel@tonic-gate 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
26757c478bd9Sstevel@tonic-gate 	(void) strcpy(svp->sv_path, root_path);
26767c478bd9Sstevel@tonic-gate 
26777c478bd9Sstevel@tonic-gate 	/*
26787c478bd9Sstevel@tonic-gate 	 * Force root partition to always be mounted with AUTH_UNIX for now
26797c478bd9Sstevel@tonic-gate 	 */
26807c478bd9Sstevel@tonic-gate 	svp->sv_secdata = kmem_alloc(sizeof (*svp->sv_secdata), KM_SLEEP);
26817c478bd9Sstevel@tonic-gate 	svp->sv_secdata->secmod = AUTH_UNIX;
26827c478bd9Sstevel@tonic-gate 	svp->sv_secdata->rpcflavor = AUTH_UNIX;
26837c478bd9Sstevel@tonic-gate 	svp->sv_secdata->data = NULL;
26847c478bd9Sstevel@tonic-gate 
26857c478bd9Sstevel@tonic-gate 	cr = crgetcred();
26867c478bd9Sstevel@tonic-gate 	rtvp = NULL;
26877c478bd9Sstevel@tonic-gate 
26887c478bd9Sstevel@tonic-gate 	error = nfs4rootvp(&rtvp, vfsp, svp, args.flags, cr, global_zone);
26897c478bd9Sstevel@tonic-gate 
26907c478bd9Sstevel@tonic-gate 	if (error) {
26917c478bd9Sstevel@tonic-gate 		crfree(cr);
26927c478bd9Sstevel@tonic-gate 		pn_free(&pn);
2693ab7762b6Smaheshvs 		sv4_free(svp);
2694ab7762b6Smaheshvs 		return (error);
26957c478bd9Sstevel@tonic-gate 	}
26967c478bd9Sstevel@tonic-gate 
26977c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rtvp);
26987c478bd9Sstevel@tonic-gate 
26997c478bd9Sstevel@tonic-gate 	/*
27007c478bd9Sstevel@tonic-gate 	 * Send client id to the server, if necessary
27017c478bd9Sstevel@tonic-gate 	 */
27027c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&n4e);
27037c478bd9Sstevel@tonic-gate 	nfs4setclientid(mi, cr, FALSE, &n4e);
27047c478bd9Sstevel@tonic-gate 	error = n4e.error;
27057c478bd9Sstevel@tonic-gate 
27067c478bd9Sstevel@tonic-gate 	crfree(cr);
27077c478bd9Sstevel@tonic-gate 
27087c478bd9Sstevel@tonic-gate 	if (error) {
27097c478bd9Sstevel@tonic-gate 		pn_free(&pn);
27107c478bd9Sstevel@tonic-gate 		goto errout;
27117c478bd9Sstevel@tonic-gate 	}
27127c478bd9Sstevel@tonic-gate 
27137c478bd9Sstevel@tonic-gate 	error = nfs4_setopts(rtvp, DATAMODEL_NATIVE, &args);
27147c478bd9Sstevel@tonic-gate 	if (error) {
27157c478bd9Sstevel@tonic-gate 		nfs_cmn_err(error, CE_WARN,
27167c478bd9Sstevel@tonic-gate 		    "nfs4_mountroot: invalid root mount options");
27177c478bd9Sstevel@tonic-gate 		pn_free(&pn);
27187c478bd9Sstevel@tonic-gate 		goto errout;
27197c478bd9Sstevel@tonic-gate 	}
27207c478bd9Sstevel@tonic-gate 
27217c478bd9Sstevel@tonic-gate 	(void) vfs_lock_wait(vfsp);
27227c478bd9Sstevel@tonic-gate 	vfs_add(NULL, vfsp, vfsflags);
27237c478bd9Sstevel@tonic-gate 	vfs_unlock(vfsp);
27247c478bd9Sstevel@tonic-gate 
27257c478bd9Sstevel@tonic-gate 	size = strlen(svp->sv_hostname);
27267c478bd9Sstevel@tonic-gate 	(void) strcpy(rootfs.bo_name, svp->sv_hostname);
27277c478bd9Sstevel@tonic-gate 	rootfs.bo_name[size] = ':';
27287c478bd9Sstevel@tonic-gate 	(void) strcpy(&rootfs.bo_name[size + 1], root_path);
27297c478bd9Sstevel@tonic-gate 
27307c478bd9Sstevel@tonic-gate 	pn_free(&pn);
27317c478bd9Sstevel@tonic-gate 
27327c478bd9Sstevel@tonic-gate errout:
27337c478bd9Sstevel@tonic-gate 	if (error) {
27347c478bd9Sstevel@tonic-gate 		sv4_free(svp);
27357c478bd9Sstevel@tonic-gate 		nfs4_async_stop(vfsp);
27367c478bd9Sstevel@tonic-gate 		nfs4_async_manager_stop(vfsp);
27377c478bd9Sstevel@tonic-gate 	}
27387c478bd9Sstevel@tonic-gate 
27397c478bd9Sstevel@tonic-gate 	if (rtvp != NULL)
27407c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
27417c478bd9Sstevel@tonic-gate 
27427c478bd9Sstevel@tonic-gate 	return (error);
27437c478bd9Sstevel@tonic-gate }
27447c478bd9Sstevel@tonic-gate 
27457c478bd9Sstevel@tonic-gate /*
27467c478bd9Sstevel@tonic-gate  * Initialization routine for VFS routines.  Should only be called once
27477c478bd9Sstevel@tonic-gate  */
27487c478bd9Sstevel@tonic-gate int
27497c478bd9Sstevel@tonic-gate nfs4_vfsinit(void)
27507c478bd9Sstevel@tonic-gate {
27517c478bd9Sstevel@tonic-gate 	mutex_init(&nfs4_syncbusy, NULL, MUTEX_DEFAULT, NULL);
27527c478bd9Sstevel@tonic-gate 	nfs4setclientid_init();
2753*b9238976Sth 	nfs4_ephemeral_init();
27547c478bd9Sstevel@tonic-gate 	return (0);
27557c478bd9Sstevel@tonic-gate }
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate void
27587c478bd9Sstevel@tonic-gate nfs4_vfsfini(void)
27597c478bd9Sstevel@tonic-gate {
2760*b9238976Sth 	nfs4_ephemeral_fini();
27617c478bd9Sstevel@tonic-gate 	nfs4setclientid_fini();
27627c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfs4_syncbusy);
27637c478bd9Sstevel@tonic-gate }
27647c478bd9Sstevel@tonic-gate 
27657c478bd9Sstevel@tonic-gate void
27667c478bd9Sstevel@tonic-gate nfs4_freevfs(vfs_t *vfsp)
27677c478bd9Sstevel@tonic-gate {
27687c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
27697c478bd9Sstevel@tonic-gate 
277050a83466Sjwahlig 	/* need to release the initial hold */
27717c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
277250a83466Sjwahlig 	MI4_RELE(mi);
27737c478bd9Sstevel@tonic-gate }
27747c478bd9Sstevel@tonic-gate 
27757c478bd9Sstevel@tonic-gate /*
27767c478bd9Sstevel@tonic-gate  * Client side SETCLIENTID and SETCLIENTID_CONFIRM
27777c478bd9Sstevel@tonic-gate  */
27787c478bd9Sstevel@tonic-gate struct nfs4_server nfs4_server_lst =
27797c478bd9Sstevel@tonic-gate 	{ &nfs4_server_lst, &nfs4_server_lst };
27807c478bd9Sstevel@tonic-gate 
27817c478bd9Sstevel@tonic-gate kmutex_t nfs4_server_lst_lock;
27827c478bd9Sstevel@tonic-gate 
27837c478bd9Sstevel@tonic-gate static void
27847c478bd9Sstevel@tonic-gate nfs4setclientid_init(void)
27857c478bd9Sstevel@tonic-gate {
27867c478bd9Sstevel@tonic-gate 	mutex_init(&nfs4_server_lst_lock, NULL, MUTEX_DEFAULT, NULL);
27877c478bd9Sstevel@tonic-gate }
27887c478bd9Sstevel@tonic-gate 
27897c478bd9Sstevel@tonic-gate static void
27907c478bd9Sstevel@tonic-gate nfs4setclientid_fini(void)
27917c478bd9Sstevel@tonic-gate {
27927c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfs4_server_lst_lock);
27937c478bd9Sstevel@tonic-gate }
27947c478bd9Sstevel@tonic-gate 
27957c478bd9Sstevel@tonic-gate int nfs4_retry_sclid_delay = NFS4_RETRY_SCLID_DELAY;
27967c478bd9Sstevel@tonic-gate int nfs4_num_sclid_retries = NFS4_NUM_SCLID_RETRIES;
27977c478bd9Sstevel@tonic-gate 
27987c478bd9Sstevel@tonic-gate /*
27997c478bd9Sstevel@tonic-gate  * Set the clientid for the server for "mi".  No-op if the clientid is
28007c478bd9Sstevel@tonic-gate  * already set.
28017c478bd9Sstevel@tonic-gate  *
28027c478bd9Sstevel@tonic-gate  * The recovery boolean should be set to TRUE if this function was called
2803a092743bSek  * by the recovery code, and FALSE otherwise.  This is used to determine
2804a092743bSek  * if we need to call nfs4_start/end_op as well as grab the mi_recovlock
2805a092743bSek  * for adding a mntinfo4_t to a nfs4_server_t.
28067c478bd9Sstevel@tonic-gate  *
28077c478bd9Sstevel@tonic-gate  * Error is returned via 'n4ep'.  If there was a 'n4ep->stat' error, then
28087c478bd9Sstevel@tonic-gate  * 'n4ep->error' is set to geterrno4(n4ep->stat).
28097c478bd9Sstevel@tonic-gate  */
28107c478bd9Sstevel@tonic-gate void
28117c478bd9Sstevel@tonic-gate nfs4setclientid(mntinfo4_t *mi, cred_t *cr, bool_t recovery, nfs4_error_t *n4ep)
28127c478bd9Sstevel@tonic-gate {
28137c478bd9Sstevel@tonic-gate 	struct nfs4_server *np;
28147c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp = mi->mi_curr_serv;
28157c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
28167c478bd9Sstevel@tonic-gate 	int num_retries = 0;
2817f64c4ae1Sdm 	bool_t retry;
28187c478bd9Sstevel@tonic-gate 	cred_t *lcr = NULL;
28197c478bd9Sstevel@tonic-gate 	int retry_inuse = 1; /* only retry once on NFS4ERR_CLID_INUSE */
28207c478bd9Sstevel@tonic-gate 	time_t lease_time = 0;
28217c478bd9Sstevel@tonic-gate 
28227c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
28237c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
28247c478bd9Sstevel@tonic-gate 	ASSERT(n4ep != NULL);
28257c478bd9Sstevel@tonic-gate 
28267c478bd9Sstevel@tonic-gate recov_retry:
2827f64c4ae1Sdm 	retry = FALSE;
28287c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(n4ep);
2829a092743bSek 	if (!recovery)
2830a092743bSek 		(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
2831a092743bSek 
2832f86c6ccaSdm 	mutex_enter(&nfs4_server_lst_lock);
2833f86c6ccaSdm 	np = servinfo4_to_nfs4_server(svp); /* This locks np if it is found */
2834f86c6ccaSdm 	mutex_exit(&nfs4_server_lst_lock);
2835f86c6ccaSdm 	if (!np) {
2836f86c6ccaSdm 		struct nfs4_server *tnp;
2837f86c6ccaSdm 		np = new_nfs4_server(svp, cr);
283816237317Sdm 		mutex_enter(&np->s_lock);
28397c478bd9Sstevel@tonic-gate 
2840f86c6ccaSdm 		mutex_enter(&nfs4_server_lst_lock);
2841f86c6ccaSdm 		tnp = servinfo4_to_nfs4_server(svp);
2842f86c6ccaSdm 		if (tnp) {
2843f86c6ccaSdm 			/*
2844f86c6ccaSdm 			 * another thread snuck in and put server on list.
2845f86c6ccaSdm 			 * since we aren't adding it to the nfs4_server_list
2846f86c6ccaSdm 			 * we need to set the ref count to 0 and destroy it.
2847f86c6ccaSdm 			 */
2848f86c6ccaSdm 			np->s_refcnt = 0;
2849f86c6ccaSdm 			destroy_nfs4_server(np);
2850f86c6ccaSdm 			np = tnp;
2851f86c6ccaSdm 		} else {
2852f86c6ccaSdm 			/*
2853f86c6ccaSdm 			 * do not give list a reference until everything
2854f86c6ccaSdm 			 * succeeds
2855f86c6ccaSdm 			 */
2856f86c6ccaSdm 			insque(np, &nfs4_server_lst);
2857f86c6ccaSdm 		}
2858f86c6ccaSdm 		mutex_exit(&nfs4_server_lst_lock);
2859f86c6ccaSdm 	}
2860f86c6ccaSdm 	ASSERT(MUTEX_HELD(&np->s_lock));
28617c478bd9Sstevel@tonic-gate 	/*
2862f86c6ccaSdm 	 * If we find the server already has N4S_CLIENTID_SET, then
2863f86c6ccaSdm 	 * just return, we've already done SETCLIENTID to that server
28647c478bd9Sstevel@tonic-gate 	 */
2865f86c6ccaSdm 	if (np->s_flags & N4S_CLIENTID_SET) {
28667c478bd9Sstevel@tonic-gate 		/* add mi to np's mntinfo4_list */
28677c478bd9Sstevel@tonic-gate 		nfs4_add_mi_to_server(np, mi);
2868a092743bSek 		if (!recovery)
2869a092743bSek 			nfs_rw_exit(&mi->mi_recovlock);
28707c478bd9Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
28717c478bd9Sstevel@tonic-gate 		nfs4_server_rele(np);
28727c478bd9Sstevel@tonic-gate 		return;
28737c478bd9Sstevel@tonic-gate 	}
2874f86c6ccaSdm 	mutex_exit(&np->s_lock);
2875f86c6ccaSdm 
28767c478bd9Sstevel@tonic-gate 
2877a092743bSek 	/*
2878a092743bSek 	 * Drop the mi_recovlock since nfs4_start_op will
2879a092743bSek 	 * acquire it again for us.
2880a092743bSek 	 */
2881f86c6ccaSdm 	if (!recovery) {
2882a092743bSek 		nfs_rw_exit(&mi->mi_recovlock);
2883a092743bSek 
28847c478bd9Sstevel@tonic-gate 		n4ep->error = nfs4_start_op(mi, NULL, NULL, &recov_state);
28857c478bd9Sstevel@tonic-gate 		if (n4ep->error) {
28867c478bd9Sstevel@tonic-gate 			nfs4_server_rele(np);
28877c478bd9Sstevel@tonic-gate 			return;
28887c478bd9Sstevel@tonic-gate 		}
28897c478bd9Sstevel@tonic-gate 	}
28907c478bd9Sstevel@tonic-gate 
28917c478bd9Sstevel@tonic-gate 	mutex_enter(&np->s_lock);
2892f86c6ccaSdm 	while (np->s_flags & N4S_CLIENTID_PEND) {
2893f86c6ccaSdm 		if (!cv_wait_sig(&np->s_clientid_pend, &np->s_lock)) {
2894f86c6ccaSdm 			mutex_exit(&np->s_lock);
2895f86c6ccaSdm 			nfs4_server_rele(np);
2896f86c6ccaSdm 			if (!recovery)
2897f86c6ccaSdm 				nfs4_end_op(mi, NULL, NULL, &recov_state,
2898f86c6ccaSdm 				    recovery);
2899f86c6ccaSdm 			n4ep->error = EINTR;
2900f86c6ccaSdm 			return;
2901f86c6ccaSdm 		}
2902f86c6ccaSdm 	}
29037c478bd9Sstevel@tonic-gate 
29047c478bd9Sstevel@tonic-gate 	if (np->s_flags & N4S_CLIENTID_SET) {
29057c478bd9Sstevel@tonic-gate 		/* XXX copied/pasted from above */
29067c478bd9Sstevel@tonic-gate 		/* add mi to np's mntinfo4_list */
29077c478bd9Sstevel@tonic-gate 		nfs4_add_mi_to_server(np, mi);
29087c478bd9Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
29097c478bd9Sstevel@tonic-gate 		nfs4_server_rele(np);
29107c478bd9Sstevel@tonic-gate 		if (!recovery)
29117c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, recovery);
29127c478bd9Sstevel@tonic-gate 		return;
29137c478bd9Sstevel@tonic-gate 	}
29147c478bd9Sstevel@tonic-gate 
2915f86c6ccaSdm 	/*
2916f86c6ccaSdm 	 * Reset the N4S_CB_PINGED flag. This is used to
2917f86c6ccaSdm 	 * indicate if we have received a CB_NULL from the
2918f86c6ccaSdm 	 * server. Also we reset the waiter flag.
2919f86c6ccaSdm 	 */
2920f86c6ccaSdm 	np->s_flags &= ~(N4S_CB_PINGED | N4S_CB_WAITER);
2921f86c6ccaSdm 	/* any failure must now clear this flag */
2922f86c6ccaSdm 	np->s_flags |= N4S_CLIENTID_PEND;
2923f86c6ccaSdm 	mutex_exit(&np->s_lock);
29247c478bd9Sstevel@tonic-gate 	nfs4setclientid_otw(mi, svp, cr, np, n4ep, &retry_inuse);
29257c478bd9Sstevel@tonic-gate 
29267c478bd9Sstevel@tonic-gate 	if (n4ep->error == EACCES) {
29277c478bd9Sstevel@tonic-gate 		/*
29287c478bd9Sstevel@tonic-gate 		 * If the uid is set then set the creds for secure mounts
29297c478bd9Sstevel@tonic-gate 		 * by proxy processes such as automountd.
29307c478bd9Sstevel@tonic-gate 		 */
29317c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
29327c478bd9Sstevel@tonic-gate 		if (svp->sv_secdata->uid != 0) {
29337c478bd9Sstevel@tonic-gate 			lcr = crdup(cr);
29347c478bd9Sstevel@tonic-gate 			(void) crsetugid(lcr, svp->sv_secdata->uid,
29357c478bd9Sstevel@tonic-gate 			    crgetgid(cr));
29367c478bd9Sstevel@tonic-gate 		}
29377c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
29387c478bd9Sstevel@tonic-gate 
2939f86c6ccaSdm 		if (lcr != NULL) {
2940f86c6ccaSdm 			mutex_enter(&np->s_lock);
2941f86c6ccaSdm 			crfree(np->s_cred);
2942f86c6ccaSdm 			np->s_cred = lcr;
2943f86c6ccaSdm 			mutex_exit(&np->s_lock);
29447c478bd9Sstevel@tonic-gate 			nfs4setclientid_otw(mi, svp, lcr, np, n4ep,
2945*b9238976Sth 			    &retry_inuse);
2946f86c6ccaSdm 		}
29477c478bd9Sstevel@tonic-gate 	}
2948f86c6ccaSdm 	mutex_enter(&np->s_lock);
29497c478bd9Sstevel@tonic-gate 	lease_time = np->s_lease_time;
2950f86c6ccaSdm 	np->s_flags &= ~N4S_CLIENTID_PEND;
29517c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
29527c478bd9Sstevel@tonic-gate 
29537c478bd9Sstevel@tonic-gate 	if (n4ep->error != 0 || n4ep->stat != NFS4_OK) {
29547c478bd9Sstevel@tonic-gate 		/*
29557c478bd9Sstevel@tonic-gate 		 * Start recovery if failover is a possibility.  If
29567c478bd9Sstevel@tonic-gate 		 * invoked by the recovery thread itself, then just
29577c478bd9Sstevel@tonic-gate 		 * return and let it handle the failover first.  NB:
29587c478bd9Sstevel@tonic-gate 		 * recovery is not allowed if the mount is in progress
29597c478bd9Sstevel@tonic-gate 		 * since the infrastructure is not sufficiently setup
29607c478bd9Sstevel@tonic-gate 		 * to allow it.  Just return the error (after suitable
29617c478bd9Sstevel@tonic-gate 		 * retries).
29627c478bd9Sstevel@tonic-gate 		 */
29637c478bd9Sstevel@tonic-gate 		if (FAILOVER_MOUNT4(mi) && nfs4_try_failover(n4ep)) {
29647c478bd9Sstevel@tonic-gate 			(void) nfs4_start_recovery(n4ep, mi, NULL,
2965*b9238976Sth 			    NULL, NULL, NULL, OP_SETCLIENTID, NULL);
29667c478bd9Sstevel@tonic-gate 			/*
29677c478bd9Sstevel@tonic-gate 			 * Don't retry here, just return and let
29687c478bd9Sstevel@tonic-gate 			 * recovery take over.
29697c478bd9Sstevel@tonic-gate 			 */
29707c478bd9Sstevel@tonic-gate 			if (recovery)
29717c478bd9Sstevel@tonic-gate 				retry = FALSE;
29727c478bd9Sstevel@tonic-gate 		} else if (nfs4_rpc_retry_error(n4ep->error) ||
2973*b9238976Sth 		    n4ep->stat == NFS4ERR_RESOURCE ||
2974*b9238976Sth 		    n4ep->stat == NFS4ERR_STALE_CLIENTID) {
29757c478bd9Sstevel@tonic-gate 
2976*b9238976Sth 			retry = TRUE;
2977*b9238976Sth 			/*
2978*b9238976Sth 			 * Always retry if in recovery or once had
2979*b9238976Sth 			 * contact with the server (but now it's
2980*b9238976Sth 			 * overloaded).
2981*b9238976Sth 			 */
2982*b9238976Sth 			if (recovery == TRUE ||
2983*b9238976Sth 			    n4ep->error == ETIMEDOUT ||
2984*b9238976Sth 			    n4ep->error == ECONNRESET)
29857c478bd9Sstevel@tonic-gate 				num_retries = 0;
2986*b9238976Sth 		} else if (retry_inuse && n4ep->error == 0 &&
2987*b9238976Sth 		    n4ep->stat == NFS4ERR_CLID_INUSE) {
2988*b9238976Sth 			retry = TRUE;
2989*b9238976Sth 			num_retries = 0;
29907c478bd9Sstevel@tonic-gate 		}
2991f86c6ccaSdm 	} else {
2992f64c4ae1Sdm 		/*
2993f64c4ae1Sdm 		 * Since everything succeeded give the list a reference count if
2994f64c4ae1Sdm 		 * it hasn't been given one by add_new_nfs4_server() or if this
2995f64c4ae1Sdm 		 * is not a recovery situation in which case it is already on
2996f64c4ae1Sdm 		 * the list.
2997f64c4ae1Sdm 		 */
2998f86c6ccaSdm 		mutex_enter(&np->s_lock);
2999f64c4ae1Sdm 		if ((np->s_flags & N4S_INSERTED) == 0) {
3000f64c4ae1Sdm 			np->s_refcnt++;
3001f64c4ae1Sdm 			np->s_flags |= N4S_INSERTED;
3002f64c4ae1Sdm 		}
3003f86c6ccaSdm 		mutex_exit(&np->s_lock);
30047c478bd9Sstevel@tonic-gate 	}
30057c478bd9Sstevel@tonic-gate 
30067c478bd9Sstevel@tonic-gate 	if (!recovery)
30077c478bd9Sstevel@tonic-gate 		nfs4_end_op(mi, NULL, NULL, &recov_state, recovery);
3008f86c6ccaSdm 
30097c478bd9Sstevel@tonic-gate 
30107c478bd9Sstevel@tonic-gate 	if (retry && num_retries++ < nfs4_num_sclid_retries) {
30117c478bd9Sstevel@tonic-gate 		if (retry_inuse) {
30127c478bd9Sstevel@tonic-gate 			delay(SEC_TO_TICK(lease_time + nfs4_retry_sclid_delay));
30137c478bd9Sstevel@tonic-gate 			retry_inuse = 0;
30147c478bd9Sstevel@tonic-gate 		} else
30157c478bd9Sstevel@tonic-gate 			delay(SEC_TO_TICK(nfs4_retry_sclid_delay));
3016f86c6ccaSdm 
3017f86c6ccaSdm 		nfs4_server_rele(np);
30187c478bd9Sstevel@tonic-gate 		goto recov_retry;
30197c478bd9Sstevel@tonic-gate 	}
30207c478bd9Sstevel@tonic-gate 
3021f86c6ccaSdm 
30227c478bd9Sstevel@tonic-gate 	if (n4ep->error == 0)
30237c478bd9Sstevel@tonic-gate 		n4ep->error = geterrno4(n4ep->stat);
3024f86c6ccaSdm 
3025f86c6ccaSdm 	/* broadcast before release in case no other threads are waiting */
3026f86c6ccaSdm 	cv_broadcast(&np->s_clientid_pend);
3027f86c6ccaSdm 	nfs4_server_rele(np);
30287c478bd9Sstevel@tonic-gate }
30297c478bd9Sstevel@tonic-gate 
30307c478bd9Sstevel@tonic-gate int nfs4setclientid_otw_debug = 0;
30317c478bd9Sstevel@tonic-gate 
30327c478bd9Sstevel@tonic-gate /*
30337c478bd9Sstevel@tonic-gate  * This function handles the recovery of STALE_CLIENTID for SETCLIENTID_CONFRIM,
30347c478bd9Sstevel@tonic-gate  * but nothing else; the calling function must be designed to handle those
30357c478bd9Sstevel@tonic-gate  * other errors.
30367c478bd9Sstevel@tonic-gate  */
30377c478bd9Sstevel@tonic-gate static void
30387c478bd9Sstevel@tonic-gate nfs4setclientid_otw(mntinfo4_t *mi, struct servinfo4 *svp,  cred_t *cr,
3039*b9238976Sth     struct nfs4_server *np, nfs4_error_t *ep, int *retry_inusep)
30407c478bd9Sstevel@tonic-gate {
30417c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
30427c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
30437c478bd9Sstevel@tonic-gate 	nfs_argop4 argop[3];
30447c478bd9Sstevel@tonic-gate 	SETCLIENTID4args *s_args;
30457c478bd9Sstevel@tonic-gate 	SETCLIENTID4resok *s_resok;
30467c478bd9Sstevel@tonic-gate 	int doqueue = 1;
30477c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t *garp = NULL;
30487c478bd9Sstevel@tonic-gate 	timespec_t prop_time, after_time;
30497c478bd9Sstevel@tonic-gate 	verifier4 verf;
30507c478bd9Sstevel@tonic-gate 	clientid4 tmp_clientid;
30517c478bd9Sstevel@tonic-gate 
3052f86c6ccaSdm 	ASSERT(!MUTEX_HELD(&np->s_lock));
30537c478bd9Sstevel@tonic-gate 
30547c478bd9Sstevel@tonic-gate 	args.ctag = TAG_SETCLIENTID;
30557c478bd9Sstevel@tonic-gate 
30567c478bd9Sstevel@tonic-gate 	args.array = argop;
30577c478bd9Sstevel@tonic-gate 	args.array_len = 3;
30587c478bd9Sstevel@tonic-gate 
30597c478bd9Sstevel@tonic-gate 	/* PUTROOTFH */
30607c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_PUTROOTFH;
30617c478bd9Sstevel@tonic-gate 
30627c478bd9Sstevel@tonic-gate 	/* GETATTR */
30637c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_GETATTR;
30647c478bd9Sstevel@tonic-gate 	argop[1].nfs_argop4_u.opgetattr.attr_request = FATTR4_LEASE_TIME_MASK;
30657c478bd9Sstevel@tonic-gate 	argop[1].nfs_argop4_u.opgetattr.mi = mi;
30667c478bd9Sstevel@tonic-gate 
30677c478bd9Sstevel@tonic-gate 	/* SETCLIENTID */
30687c478bd9Sstevel@tonic-gate 	argop[2].argop = OP_SETCLIENTID;
30697c478bd9Sstevel@tonic-gate 
30707c478bd9Sstevel@tonic-gate 	s_args = &argop[2].nfs_argop4_u.opsetclientid;
30717c478bd9Sstevel@tonic-gate 
3072f86c6ccaSdm 	mutex_enter(&np->s_lock);
3073f86c6ccaSdm 
30747c478bd9Sstevel@tonic-gate 	s_args->client.verifier = np->clidtosend.verifier;
30757c478bd9Sstevel@tonic-gate 	s_args->client.id_len = np->clidtosend.id_len;
30767c478bd9Sstevel@tonic-gate 	ASSERT(s_args->client.id_len <= NFS4_OPAQUE_LIMIT);
30777c478bd9Sstevel@tonic-gate 	s_args->client.id_val = np->clidtosend.id_val;
30787c478bd9Sstevel@tonic-gate 
30797c478bd9Sstevel@tonic-gate 	/*
30807c478bd9Sstevel@tonic-gate 	 * Callback needs to happen on non-RDMA transport
30817c478bd9Sstevel@tonic-gate 	 * Check if we have saved the original knetconfig
30827c478bd9Sstevel@tonic-gate 	 * if so, use that instead.
30837c478bd9Sstevel@tonic-gate 	 */
30847c478bd9Sstevel@tonic-gate 	if (svp->sv_origknconf != NULL)
30857c478bd9Sstevel@tonic-gate 		nfs4_cb_args(np, svp->sv_origknconf, s_args);
30867c478bd9Sstevel@tonic-gate 	else
30877c478bd9Sstevel@tonic-gate 		nfs4_cb_args(np, svp->sv_knconf, s_args);
30887c478bd9Sstevel@tonic-gate 
3089f86c6ccaSdm 	mutex_exit(&np->s_lock);
3090f86c6ccaSdm 
3091f86c6ccaSdm 	rfs4call(mi, &args, &res, cr, &doqueue, 0, ep);
30927c478bd9Sstevel@tonic-gate 
30937c478bd9Sstevel@tonic-gate 	if (ep->error)
30947c478bd9Sstevel@tonic-gate 		return;
30957c478bd9Sstevel@tonic-gate 
30967c478bd9Sstevel@tonic-gate 	/* getattr lease_time res */
30977c478bd9Sstevel@tonic-gate 	if (res.array_len >= 2) {
30987c478bd9Sstevel@tonic-gate 		garp = &res.array[1].nfs_resop4_u.opgetattr.ga_res;
30997c478bd9Sstevel@tonic-gate 
31007c478bd9Sstevel@tonic-gate #ifndef _LP64
31017c478bd9Sstevel@tonic-gate 		/*
31027c478bd9Sstevel@tonic-gate 		 * The 32 bit client cannot handle a lease time greater than
31037c478bd9Sstevel@tonic-gate 		 * (INT32_MAX/1000000).  This is due to the use of the
31047c478bd9Sstevel@tonic-gate 		 * lease_time in calls to drv_usectohz() in
31057c478bd9Sstevel@tonic-gate 		 * nfs4_renew_lease_thread().  The problem is that
31067c478bd9Sstevel@tonic-gate 		 * drv_usectohz() takes a time_t (which is just a long = 4
31077c478bd9Sstevel@tonic-gate 		 * bytes) as its parameter.  The lease_time is multiplied by
31087c478bd9Sstevel@tonic-gate 		 * 1000000 to convert seconds to usecs for the parameter.  If
31097c478bd9Sstevel@tonic-gate 		 * a number bigger than (INT32_MAX/1000000) is used then we
31107c478bd9Sstevel@tonic-gate 		 * overflow on the 32bit client.
31117c478bd9Sstevel@tonic-gate 		 */
31127c478bd9Sstevel@tonic-gate 		if (garp->n4g_ext_res->n4g_leasetime > (INT32_MAX/1000000)) {
31137c478bd9Sstevel@tonic-gate 			garp->n4g_ext_res->n4g_leasetime = INT32_MAX/1000000;
31147c478bd9Sstevel@tonic-gate 		}
31157c478bd9Sstevel@tonic-gate #endif
31167c478bd9Sstevel@tonic-gate 
3117f86c6ccaSdm 		mutex_enter(&np->s_lock);
31187c478bd9Sstevel@tonic-gate 		np->s_lease_time = garp->n4g_ext_res->n4g_leasetime;
31197c478bd9Sstevel@tonic-gate 
31207c478bd9Sstevel@tonic-gate 		/*
31217c478bd9Sstevel@tonic-gate 		 * Keep track of the lease period for the mi's
31227c478bd9Sstevel@tonic-gate 		 * mi_msg_list.  We need an appropiate time
31237c478bd9Sstevel@tonic-gate 		 * bound to associate past facts with a current
31247c478bd9Sstevel@tonic-gate 		 * event.  The lease period is perfect for this.
31257c478bd9Sstevel@tonic-gate 		 */
31267c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_msg_list_lock);
31277c478bd9Sstevel@tonic-gate 		mi->mi_lease_period = np->s_lease_time;
31287c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_msg_list_lock);
3129f86c6ccaSdm 		mutex_exit(&np->s_lock);
31307c478bd9Sstevel@tonic-gate 	}
31317c478bd9Sstevel@tonic-gate 
31327c478bd9Sstevel@tonic-gate 
31337c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_CLID_INUSE) {
31347c478bd9Sstevel@tonic-gate 		clientaddr4 *clid_inuse;
31357c478bd9Sstevel@tonic-gate 
31367c478bd9Sstevel@tonic-gate 		if (!(*retry_inusep)) {
31377c478bd9Sstevel@tonic-gate 			clid_inuse = &res.array->nfs_resop4_u.
3138*b9238976Sth 			    opsetclientid.SETCLIENTID4res_u.client_using;
31397c478bd9Sstevel@tonic-gate 
31407c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
31417c478bd9Sstevel@tonic-gate 			    "NFS4 mount (SETCLIENTID failed)."
31427c478bd9Sstevel@tonic-gate 			    "  nfs4_client_id.id is in"
31437c478bd9Sstevel@tonic-gate 			    "use already by: r_netid<%s> r_addr<%s>",
31447c478bd9Sstevel@tonic-gate 			    clid_inuse->r_netid, clid_inuse->r_addr);
31457c478bd9Sstevel@tonic-gate 		}
31467c478bd9Sstevel@tonic-gate 
31477c478bd9Sstevel@tonic-gate 		/*
31487c478bd9Sstevel@tonic-gate 		 * XXX - The client should be more robust in its
31497c478bd9Sstevel@tonic-gate 		 * handling of clientid in use errors (regen another
31507c478bd9Sstevel@tonic-gate 		 * clientid and try again?)
31517c478bd9Sstevel@tonic-gate 		 */
31527c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
31537c478bd9Sstevel@tonic-gate 		return;
31547c478bd9Sstevel@tonic-gate 	}
31557c478bd9Sstevel@tonic-gate 
31567c478bd9Sstevel@tonic-gate 	if (res.status) {
31577c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
31587c478bd9Sstevel@tonic-gate 		return;
31597c478bd9Sstevel@tonic-gate 	}
31607c478bd9Sstevel@tonic-gate 
31617c478bd9Sstevel@tonic-gate 	s_resok = &res.array[2].nfs_resop4_u.
3162*b9238976Sth 	    opsetclientid.SETCLIENTID4res_u.resok4;
31637c478bd9Sstevel@tonic-gate 
31647c478bd9Sstevel@tonic-gate 	tmp_clientid = s_resok->clientid;
31657c478bd9Sstevel@tonic-gate 
31667c478bd9Sstevel@tonic-gate 	verf = s_resok->setclientid_confirm;
31677c478bd9Sstevel@tonic-gate 
31687c478bd9Sstevel@tonic-gate #ifdef	DEBUG
31697c478bd9Sstevel@tonic-gate 	if (nfs4setclientid_otw_debug) {
31707c478bd9Sstevel@tonic-gate 		union {
31717c478bd9Sstevel@tonic-gate 			clientid4	clientid;
31727c478bd9Sstevel@tonic-gate 			int		foo[2];
31737c478bd9Sstevel@tonic-gate 		} cid;
31747c478bd9Sstevel@tonic-gate 
31757c478bd9Sstevel@tonic-gate 		cid.clientid = s_resok->clientid;
31767c478bd9Sstevel@tonic-gate 
31777c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
31787c478bd9Sstevel@tonic-gate 		"nfs4setclientid_otw: OK, clientid = %x,%x, "
31797c478bd9Sstevel@tonic-gate 		"verifier = %" PRIx64 "\n", cid.foo[0], cid.foo[1], verf);
31807c478bd9Sstevel@tonic-gate 	}
31817c478bd9Sstevel@tonic-gate #endif
31827c478bd9Sstevel@tonic-gate 
31837c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
31847c478bd9Sstevel@tonic-gate 
31857c478bd9Sstevel@tonic-gate 	/* Confirm the client id and get the lease_time attribute */
31867c478bd9Sstevel@tonic-gate 
31877c478bd9Sstevel@tonic-gate 	args.ctag = TAG_SETCLIENTID_CF;
31887c478bd9Sstevel@tonic-gate 
31897c478bd9Sstevel@tonic-gate 	args.array = argop;
31907c478bd9Sstevel@tonic-gate 	args.array_len = 1;
31917c478bd9Sstevel@tonic-gate 
31927c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_SETCLIENTID_CONFIRM;
31937c478bd9Sstevel@tonic-gate 
31947c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opsetclientid_confirm.clientid = tmp_clientid;
31957c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opsetclientid_confirm.setclientid_confirm = verf;
31967c478bd9Sstevel@tonic-gate 
31977c478bd9Sstevel@tonic-gate 	/* used to figure out RTT for np */
31987c478bd9Sstevel@tonic-gate 	gethrestime(&prop_time);
31997c478bd9Sstevel@tonic-gate 
32007c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setlientid_otw: "
3201*b9238976Sth 	    "start time: %ld sec %ld nsec", prop_time.tv_sec,
3202*b9238976Sth 	    prop_time.tv_nsec));
32037c478bd9Sstevel@tonic-gate 
32047c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, 0, ep);
32057c478bd9Sstevel@tonic-gate 
32067c478bd9Sstevel@tonic-gate 	gethrestime(&after_time);
3207f86c6ccaSdm 	mutex_enter(&np->s_lock);
32087c478bd9Sstevel@tonic-gate 	np->propagation_delay.tv_sec =
3209*b9238976Sth 	    MAX(1, after_time.tv_sec - prop_time.tv_sec);
3210f86c6ccaSdm 	mutex_exit(&np->s_lock);
32117c478bd9Sstevel@tonic-gate 
32127c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setlcientid_otw: "
3213*b9238976Sth 	    "finish time: %ld sec ", after_time.tv_sec));
32147c478bd9Sstevel@tonic-gate 
32157c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setclientid_otw: "
3216*b9238976Sth 	    "propagation delay set to %ld sec",
3217*b9238976Sth 	    np->propagation_delay.tv_sec));
32187c478bd9Sstevel@tonic-gate 
32197c478bd9Sstevel@tonic-gate 	if (ep->error)
32207c478bd9Sstevel@tonic-gate 		return;
32217c478bd9Sstevel@tonic-gate 
32227c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_CLID_INUSE) {
32237c478bd9Sstevel@tonic-gate 		clientaddr4 *clid_inuse;
32247c478bd9Sstevel@tonic-gate 
32257c478bd9Sstevel@tonic-gate 		if (!(*retry_inusep)) {
32267c478bd9Sstevel@tonic-gate 			clid_inuse = &res.array->nfs_resop4_u.
3227*b9238976Sth 			    opsetclientid.SETCLIENTID4res_u.client_using;
32287c478bd9Sstevel@tonic-gate 
32297c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
32307c478bd9Sstevel@tonic-gate 			    "SETCLIENTID_CONFIRM failed.  "
32317c478bd9Sstevel@tonic-gate 			    "nfs4_client_id.id is in use already by: "
32327c478bd9Sstevel@tonic-gate 			    "r_netid<%s> r_addr<%s>",
32337c478bd9Sstevel@tonic-gate 			    clid_inuse->r_netid, clid_inuse->r_addr);
32347c478bd9Sstevel@tonic-gate 		}
32357c478bd9Sstevel@tonic-gate 
32367c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
32377c478bd9Sstevel@tonic-gate 		return;
32387c478bd9Sstevel@tonic-gate 	}
32397c478bd9Sstevel@tonic-gate 
32407c478bd9Sstevel@tonic-gate 	if (res.status) {
32417c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
32427c478bd9Sstevel@tonic-gate 		return;
32437c478bd9Sstevel@tonic-gate 	}
32447c478bd9Sstevel@tonic-gate 
3245f86c6ccaSdm 	mutex_enter(&np->s_lock);
32467c478bd9Sstevel@tonic-gate 	np->clientid = tmp_clientid;
32477c478bd9Sstevel@tonic-gate 	np->s_flags |= N4S_CLIENTID_SET;
32487c478bd9Sstevel@tonic-gate 
32497c478bd9Sstevel@tonic-gate 	/* Add mi to np's mntinfo4 list */
32507c478bd9Sstevel@tonic-gate 	nfs4_add_mi_to_server(np, mi);
32517c478bd9Sstevel@tonic-gate 
32527c478bd9Sstevel@tonic-gate 	if (np->lease_valid == NFS4_LEASE_NOT_STARTED) {
32537c478bd9Sstevel@tonic-gate 		/*
32547c478bd9Sstevel@tonic-gate 		 * Start lease management thread.
32557c478bd9Sstevel@tonic-gate 		 * Keep trying until we succeed.
32567c478bd9Sstevel@tonic-gate 		 */
32577c478bd9Sstevel@tonic-gate 
32587c478bd9Sstevel@tonic-gate 		np->s_refcnt++;		/* pass reference to thread */
32597c478bd9Sstevel@tonic-gate 		(void) zthread_create(NULL, 0, nfs4_renew_lease_thread, np, 0,
3260*b9238976Sth 		    minclsyspri);
32617c478bd9Sstevel@tonic-gate 	}
3262f86c6ccaSdm 	mutex_exit(&np->s_lock);
32637c478bd9Sstevel@tonic-gate 
32647c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
32657c478bd9Sstevel@tonic-gate }
32667c478bd9Sstevel@tonic-gate 
32677c478bd9Sstevel@tonic-gate /*
32687c478bd9Sstevel@tonic-gate  * Add mi to sp's mntinfo4_list if it isn't already in the list.  Makes
32697c478bd9Sstevel@tonic-gate  * mi's clientid the same as sp's.
32707c478bd9Sstevel@tonic-gate  * Assumes sp is locked down.
32717c478bd9Sstevel@tonic-gate  */
32727c478bd9Sstevel@tonic-gate void
32737c478bd9Sstevel@tonic-gate nfs4_add_mi_to_server(nfs4_server_t *sp, mntinfo4_t *mi)
32747c478bd9Sstevel@tonic-gate {
32757c478bd9Sstevel@tonic-gate 	mntinfo4_t *tmi;
32767c478bd9Sstevel@tonic-gate 	int in_list = 0;
32777c478bd9Sstevel@tonic-gate 
3278a092743bSek 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
3279a092743bSek 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
32807c478bd9Sstevel@tonic-gate 	ASSERT(sp != &nfs4_server_lst);
32817c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
32827c478bd9Sstevel@tonic-gate 
32837c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3284*b9238976Sth 	    "nfs4_add_mi_to_server: add mi %p to sp %p",
3285*b9238976Sth 	    (void*)mi, (void*)sp));
32867c478bd9Sstevel@tonic-gate 
32877c478bd9Sstevel@tonic-gate 	for (tmi = sp->mntinfo4_list;
32887c478bd9Sstevel@tonic-gate 	    tmi != NULL;
32897c478bd9Sstevel@tonic-gate 	    tmi = tmi->mi_clientid_next) {
32907c478bd9Sstevel@tonic-gate 		if (tmi == mi) {
32917c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_lease_debug,
3292*b9238976Sth 			    (CE_NOTE,
3293*b9238976Sth 			    "nfs4_add_mi_to_server: mi in list"));
32947c478bd9Sstevel@tonic-gate 			in_list = 1;
32957c478bd9Sstevel@tonic-gate 		}
32967c478bd9Sstevel@tonic-gate 	}
32977c478bd9Sstevel@tonic-gate 
32987c478bd9Sstevel@tonic-gate 	/*
32997c478bd9Sstevel@tonic-gate 	 * First put a hold on the mntinfo4's vfsp so that references via
33007c478bd9Sstevel@tonic-gate 	 * mntinfo4_list will be valid.
33017c478bd9Sstevel@tonic-gate 	 */
33027c478bd9Sstevel@tonic-gate 	if (!in_list)
33037c478bd9Sstevel@tonic-gate 		VFS_HOLD(mi->mi_vfsp);
33047c478bd9Sstevel@tonic-gate 
33057c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4_add_mi_to_server: "
3306*b9238976Sth 	    "hold vfs %p for mi: %p", (void*)mi->mi_vfsp, (void*)mi));
33077c478bd9Sstevel@tonic-gate 
33087c478bd9Sstevel@tonic-gate 	if (!in_list) {
33097c478bd9Sstevel@tonic-gate 		if (sp->mntinfo4_list)
33107c478bd9Sstevel@tonic-gate 			sp->mntinfo4_list->mi_clientid_prev = mi;
33117c478bd9Sstevel@tonic-gate 		mi->mi_clientid_next = sp->mntinfo4_list;
33127c478bd9Sstevel@tonic-gate 		sp->mntinfo4_list = mi;
33137c478bd9Sstevel@tonic-gate 		mi->mi_srvsettime = gethrestime_sec();
33147c478bd9Sstevel@tonic-gate 	}
33157c478bd9Sstevel@tonic-gate 
33167c478bd9Sstevel@tonic-gate 	/* set mi's clientid to that of sp's for later matching */
33177c478bd9Sstevel@tonic-gate 	mi->mi_clientid = sp->clientid;
33187c478bd9Sstevel@tonic-gate 
33197c478bd9Sstevel@tonic-gate 	/*
33207c478bd9Sstevel@tonic-gate 	 * Update the clientid for any other mi's belonging to sp.  This
33217c478bd9Sstevel@tonic-gate 	 * must be done here while we hold sp->s_lock, so that
33227c478bd9Sstevel@tonic-gate 	 * find_nfs4_server() continues to work.
33237c478bd9Sstevel@tonic-gate 	 */
33247c478bd9Sstevel@tonic-gate 
33257c478bd9Sstevel@tonic-gate 	for (tmi = sp->mntinfo4_list;
33267c478bd9Sstevel@tonic-gate 	    tmi != NULL;
33277c478bd9Sstevel@tonic-gate 	    tmi = tmi->mi_clientid_next) {
33287c478bd9Sstevel@tonic-gate 		if (tmi != mi) {
33297c478bd9Sstevel@tonic-gate 			tmi->mi_clientid = sp->clientid;
33307c478bd9Sstevel@tonic-gate 		}
33317c478bd9Sstevel@tonic-gate 	}
33327c478bd9Sstevel@tonic-gate }
33337c478bd9Sstevel@tonic-gate 
33347c478bd9Sstevel@tonic-gate /*
33357c478bd9Sstevel@tonic-gate  * Remove the mi from sp's mntinfo4_list and release its reference.
33367c478bd9Sstevel@tonic-gate  * Exception: if mi still has open files, flag it for later removal (when
33377c478bd9Sstevel@tonic-gate  * all the files are closed).
33387c478bd9Sstevel@tonic-gate  *
33397c478bd9Sstevel@tonic-gate  * If this is the last mntinfo4 in sp's list then tell the lease renewal
33407c478bd9Sstevel@tonic-gate  * thread to exit.
33417c478bd9Sstevel@tonic-gate  */
33427c478bd9Sstevel@tonic-gate static void
33437c478bd9Sstevel@tonic-gate nfs4_remove_mi_from_server_nolock(mntinfo4_t *mi, nfs4_server_t *sp)
33447c478bd9Sstevel@tonic-gate {
33457c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3346*b9238976Sth 	    "nfs4_remove_mi_from_server_nolock: remove mi %p from sp %p",
3347*b9238976Sth 	    (void*)mi, (void*)sp));
33487c478bd9Sstevel@tonic-gate 
33497c478bd9Sstevel@tonic-gate 	ASSERT(sp != NULL);
33507c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
33517c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_open_files >= 0);
33527c478bd9Sstevel@tonic-gate 
33537c478bd9Sstevel@tonic-gate 	/*
33547c478bd9Sstevel@tonic-gate 	 * First make sure this mntinfo4 can be taken off of the list,
33557c478bd9Sstevel@tonic-gate 	 * ie: it doesn't have any open files remaining.
33567c478bd9Sstevel@tonic-gate 	 */
33577c478bd9Sstevel@tonic-gate 	if (mi->mi_open_files > 0) {
33587c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3359*b9238976Sth 		    "nfs4_remove_mi_from_server_nolock: don't "
3360*b9238976Sth 		    "remove mi since it still has files open"));
33617c478bd9Sstevel@tonic-gate 
33627c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
33637c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_REMOVE_ON_LAST_CLOSE;
33647c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
33657c478bd9Sstevel@tonic-gate 		return;
33667c478bd9Sstevel@tonic-gate 	}
33677c478bd9Sstevel@tonic-gate 
336850a83466Sjwahlig 	VFS_HOLD(mi->mi_vfsp);
33697c478bd9Sstevel@tonic-gate 	remove_mi(sp, mi);
337050a83466Sjwahlig 	VFS_RELE(mi->mi_vfsp);
33717c478bd9Sstevel@tonic-gate 
33727c478bd9Sstevel@tonic-gate 	if (sp->mntinfo4_list == NULL) {
33737c478bd9Sstevel@tonic-gate 		/* last fs unmounted, kill the thread */
33747c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3375*b9238976Sth 		    "remove_mi_from_nfs4_server_nolock: kill the thread"));
33767c478bd9Sstevel@tonic-gate 		nfs4_mark_srv_dead(sp);
33777c478bd9Sstevel@tonic-gate 	}
33787c478bd9Sstevel@tonic-gate }
33797c478bd9Sstevel@tonic-gate 
33807c478bd9Sstevel@tonic-gate /*
33817c478bd9Sstevel@tonic-gate  * Remove mi from sp's mntinfo4_list and release the vfs reference.
33827c478bd9Sstevel@tonic-gate  */
33837c478bd9Sstevel@tonic-gate static void
33847c478bd9Sstevel@tonic-gate remove_mi(nfs4_server_t *sp, mntinfo4_t *mi)
33857c478bd9Sstevel@tonic-gate {
33867c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
33877c478bd9Sstevel@tonic-gate 
33887c478bd9Sstevel@tonic-gate 	/*
33897c478bd9Sstevel@tonic-gate 	 * We release a reference, and the caller must still have a
33907c478bd9Sstevel@tonic-gate 	 * reference.
33917c478bd9Sstevel@tonic-gate 	 */
33927c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_vfsp->vfs_count >= 2);
33937c478bd9Sstevel@tonic-gate 
33947c478bd9Sstevel@tonic-gate 	if (mi->mi_clientid_prev) {
33957c478bd9Sstevel@tonic-gate 		mi->mi_clientid_prev->mi_clientid_next = mi->mi_clientid_next;
33967c478bd9Sstevel@tonic-gate 	} else {
33977c478bd9Sstevel@tonic-gate 		/* This is the first mi in sp's mntinfo4_list */
33987c478bd9Sstevel@tonic-gate 		/*
33997c478bd9Sstevel@tonic-gate 		 * Make sure the first mntinfo4 in the list is the actual
34007c478bd9Sstevel@tonic-gate 		 * mntinfo4 passed in.
34017c478bd9Sstevel@tonic-gate 		 */
34027c478bd9Sstevel@tonic-gate 		ASSERT(sp->mntinfo4_list == mi);
34037c478bd9Sstevel@tonic-gate 
34047c478bd9Sstevel@tonic-gate 		sp->mntinfo4_list = mi->mi_clientid_next;
34057c478bd9Sstevel@tonic-gate 	}
34067c478bd9Sstevel@tonic-gate 	if (mi->mi_clientid_next)
34077c478bd9Sstevel@tonic-gate 		mi->mi_clientid_next->mi_clientid_prev = mi->mi_clientid_prev;
34087c478bd9Sstevel@tonic-gate 
34097c478bd9Sstevel@tonic-gate 	/* Now mark the mntinfo4's links as being removed */
34107c478bd9Sstevel@tonic-gate 	mi->mi_clientid_prev = mi->mi_clientid_next = NULL;
34117c478bd9Sstevel@tonic-gate 
34127c478bd9Sstevel@tonic-gate 	VFS_RELE(mi->mi_vfsp);
34137c478bd9Sstevel@tonic-gate }
34147c478bd9Sstevel@tonic-gate 
34157c478bd9Sstevel@tonic-gate /*
34167c478bd9Sstevel@tonic-gate  * Free all the entries in sp's mntinfo4_list.
34177c478bd9Sstevel@tonic-gate  */
34187c478bd9Sstevel@tonic-gate static void
34197c478bd9Sstevel@tonic-gate remove_all_mi(nfs4_server_t *sp)
34207c478bd9Sstevel@tonic-gate {
34217c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
34227c478bd9Sstevel@tonic-gate 
34237c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
34247c478bd9Sstevel@tonic-gate 
34257c478bd9Sstevel@tonic-gate 	while (sp->mntinfo4_list != NULL) {
34267c478bd9Sstevel@tonic-gate 		mi = sp->mntinfo4_list;
34277c478bd9Sstevel@tonic-gate 		/*
34287c478bd9Sstevel@tonic-gate 		 * Grab a reference in case there is only one left (which
34297c478bd9Sstevel@tonic-gate 		 * remove_mi() frees).
34307c478bd9Sstevel@tonic-gate 		 */
34317c478bd9Sstevel@tonic-gate 		VFS_HOLD(mi->mi_vfsp);
34327c478bd9Sstevel@tonic-gate 		remove_mi(sp, mi);
34337c478bd9Sstevel@tonic-gate 		VFS_RELE(mi->mi_vfsp);
34347c478bd9Sstevel@tonic-gate 	}
34357c478bd9Sstevel@tonic-gate }
34367c478bd9Sstevel@tonic-gate 
34377c478bd9Sstevel@tonic-gate /*
34387c478bd9Sstevel@tonic-gate  * Remove the mi from sp's mntinfo4_list as above, and rele the vfs.
34397c478bd9Sstevel@tonic-gate  *
34407c478bd9Sstevel@tonic-gate  * This version can be called with a null nfs4_server_t arg,
34417c478bd9Sstevel@tonic-gate  * and will either find the right one and handle locking, or
34427c478bd9Sstevel@tonic-gate  * do nothing because the mi wasn't added to an sp's mntinfo4_list.
34437c478bd9Sstevel@tonic-gate  */
34447c478bd9Sstevel@tonic-gate void
34457c478bd9Sstevel@tonic-gate nfs4_remove_mi_from_server(mntinfo4_t *mi, nfs4_server_t *esp)
34467c478bd9Sstevel@tonic-gate {
34477c478bd9Sstevel@tonic-gate 	nfs4_server_t	*sp;
34487c478bd9Sstevel@tonic-gate 
34497c478bd9Sstevel@tonic-gate 	if (esp == NULL) {
34507c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
34517c478bd9Sstevel@tonic-gate 		sp = find_nfs4_server_all(mi, 1);
34527c478bd9Sstevel@tonic-gate 	} else
34537c478bd9Sstevel@tonic-gate 		sp = esp;
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 	if (sp != NULL)
34567c478bd9Sstevel@tonic-gate 		nfs4_remove_mi_from_server_nolock(mi, sp);
34577c478bd9Sstevel@tonic-gate 
34587c478bd9Sstevel@tonic-gate 	/*
34597c478bd9Sstevel@tonic-gate 	 * If we had a valid esp as input, the calling function will be
34607c478bd9Sstevel@tonic-gate 	 * responsible for unlocking the esp nfs4_server.
34617c478bd9Sstevel@tonic-gate 	 */
34627c478bd9Sstevel@tonic-gate 	if (esp == NULL) {
34637c478bd9Sstevel@tonic-gate 		if (sp != NULL)
34647c478bd9Sstevel@tonic-gate 			mutex_exit(&sp->s_lock);
34657c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&mi->mi_recovlock);
34667c478bd9Sstevel@tonic-gate 		if (sp != NULL)
34677c478bd9Sstevel@tonic-gate 			nfs4_server_rele(sp);
34687c478bd9Sstevel@tonic-gate 	}
34697c478bd9Sstevel@tonic-gate }
34707c478bd9Sstevel@tonic-gate 
34717c478bd9Sstevel@tonic-gate /*
34727c478bd9Sstevel@tonic-gate  * Return TRUE if the given server has any non-unmounted filesystems.
34737c478bd9Sstevel@tonic-gate  */
34747c478bd9Sstevel@tonic-gate 
34757c478bd9Sstevel@tonic-gate bool_t
34767c478bd9Sstevel@tonic-gate nfs4_fs_active(nfs4_server_t *sp)
34777c478bd9Sstevel@tonic-gate {
34787c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
34797c478bd9Sstevel@tonic-gate 
34807c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
34817c478bd9Sstevel@tonic-gate 
34827c478bd9Sstevel@tonic-gate 	for (mi = sp->mntinfo4_list; mi != NULL; mi = mi->mi_clientid_next) {
34837c478bd9Sstevel@tonic-gate 		if (!(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED))
34847c478bd9Sstevel@tonic-gate 			return (TRUE);
34857c478bd9Sstevel@tonic-gate 	}
34867c478bd9Sstevel@tonic-gate 
34877c478bd9Sstevel@tonic-gate 	return (FALSE);
34887c478bd9Sstevel@tonic-gate }
34897c478bd9Sstevel@tonic-gate 
34907c478bd9Sstevel@tonic-gate /*
34917c478bd9Sstevel@tonic-gate  * Mark sp as finished and notify any waiters.
34927c478bd9Sstevel@tonic-gate  */
34937c478bd9Sstevel@tonic-gate 
34947c478bd9Sstevel@tonic-gate void
34957c478bd9Sstevel@tonic-gate nfs4_mark_srv_dead(nfs4_server_t *sp)
34967c478bd9Sstevel@tonic-gate {
34977c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
34987c478bd9Sstevel@tonic-gate 
34997c478bd9Sstevel@tonic-gate 	sp->s_thread_exit = NFS4_THREAD_EXIT;
35007c478bd9Sstevel@tonic-gate 	cv_broadcast(&sp->cv_thread_exit);
35017c478bd9Sstevel@tonic-gate }
35027c478bd9Sstevel@tonic-gate 
35037c478bd9Sstevel@tonic-gate /*
35047c478bd9Sstevel@tonic-gate  * Create a new nfs4_server_t structure.
35057c478bd9Sstevel@tonic-gate  * Returns new node unlocked and not in list, but with a reference count of
35067c478bd9Sstevel@tonic-gate  * 1.
35077c478bd9Sstevel@tonic-gate  */
35087c478bd9Sstevel@tonic-gate struct nfs4_server *
35097c478bd9Sstevel@tonic-gate new_nfs4_server(struct servinfo4 *svp, cred_t *cr)
35107c478bd9Sstevel@tonic-gate {
35117c478bd9Sstevel@tonic-gate 	struct nfs4_server *np;
35127c478bd9Sstevel@tonic-gate 	timespec_t tt;
35137c478bd9Sstevel@tonic-gate 	union {
35147c478bd9Sstevel@tonic-gate 		struct {
35157c478bd9Sstevel@tonic-gate 			uint32_t sec;
35167c478bd9Sstevel@tonic-gate 			uint32_t subsec;
35177c478bd9Sstevel@tonic-gate 		} un_curtime;
35187c478bd9Sstevel@tonic-gate 		verifier4	un_verifier;
35197c478bd9Sstevel@tonic-gate 	} nfs4clientid_verifier;
35207c478bd9Sstevel@tonic-gate 	char id_val[] = "Solaris: %s, NFSv4 kernel client";
35217c478bd9Sstevel@tonic-gate 	int len;
35227c478bd9Sstevel@tonic-gate 
35237c478bd9Sstevel@tonic-gate 	np = kmem_zalloc(sizeof (struct nfs4_server), KM_SLEEP);
35247c478bd9Sstevel@tonic-gate 	np->saddr.len = svp->sv_addr.len;
35257c478bd9Sstevel@tonic-gate 	np->saddr.maxlen = svp->sv_addr.maxlen;
35267c478bd9Sstevel@tonic-gate 	np->saddr.buf = kmem_alloc(svp->sv_addr.maxlen, KM_SLEEP);
35277c478bd9Sstevel@tonic-gate 	bcopy(svp->sv_addr.buf, np->saddr.buf, svp->sv_addr.len);
35287c478bd9Sstevel@tonic-gate 	np->s_refcnt = 1;
35297c478bd9Sstevel@tonic-gate 
35307c478bd9Sstevel@tonic-gate 	/*
35317c478bd9Sstevel@tonic-gate 	 * Build the nfs_client_id4 for this server mount.  Ensure
35327c478bd9Sstevel@tonic-gate 	 * the verifier is useful and that the identification is
35337c478bd9Sstevel@tonic-gate 	 * somehow based on the server's address for the case of
35347c478bd9Sstevel@tonic-gate 	 * multi-homed servers.
35357c478bd9Sstevel@tonic-gate 	 */
35367c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_verifier = 0;
35377c478bd9Sstevel@tonic-gate 	gethrestime(&tt);
35387c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_curtime.sec = (uint32_t)tt.tv_sec;
35397c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_curtime.subsec = (uint32_t)tt.tv_nsec;
35407c478bd9Sstevel@tonic-gate 	np->clidtosend.verifier = nfs4clientid_verifier.un_verifier;
35417c478bd9Sstevel@tonic-gate 
35427c478bd9Sstevel@tonic-gate 	/*
35437c478bd9Sstevel@tonic-gate 	 * calculate the length of the opaque identifier.  Subtract 2
35447c478bd9Sstevel@tonic-gate 	 * for the "%s" and add the traditional +1 for null
35457c478bd9Sstevel@tonic-gate 	 * termination.
35467c478bd9Sstevel@tonic-gate 	 */
35477c478bd9Sstevel@tonic-gate 	len = strlen(id_val) - 2 + strlen(uts_nodename()) + 1;
35487c478bd9Sstevel@tonic-gate 	np->clidtosend.id_len = len + np->saddr.maxlen;
35497c478bd9Sstevel@tonic-gate 
35507c478bd9Sstevel@tonic-gate 	np->clidtosend.id_val = kmem_alloc(np->clidtosend.id_len, KM_SLEEP);
35517c478bd9Sstevel@tonic-gate 	(void) sprintf(np->clidtosend.id_val, id_val, uts_nodename());
35527c478bd9Sstevel@tonic-gate 	bcopy(np->saddr.buf, &np->clidtosend.id_val[len], np->saddr.len);
35537c478bd9Sstevel@tonic-gate 
35547c478bd9Sstevel@tonic-gate 	np->s_flags = 0;
35557c478bd9Sstevel@tonic-gate 	np->mntinfo4_list = NULL;
35567c478bd9Sstevel@tonic-gate 	/* save cred for issuing rfs4calls inside the renew thread */
35577c478bd9Sstevel@tonic-gate 	crhold(cr);
35587c478bd9Sstevel@tonic-gate 	np->s_cred = cr;
35597c478bd9Sstevel@tonic-gate 	cv_init(&np->cv_thread_exit, NULL, CV_DEFAULT, NULL);
35607c478bd9Sstevel@tonic-gate 	mutex_init(&np->s_lock, NULL, MUTEX_DEFAULT, NULL);
35617c478bd9Sstevel@tonic-gate 	nfs_rw_init(&np->s_recovlock, NULL, RW_DEFAULT, NULL);
35627c478bd9Sstevel@tonic-gate 	list_create(&np->s_deleg_list, sizeof (rnode4_t),
35637c478bd9Sstevel@tonic-gate 	    offsetof(rnode4_t, r_deleg_link));
35647c478bd9Sstevel@tonic-gate 	np->s_thread_exit = 0;
35657c478bd9Sstevel@tonic-gate 	np->state_ref_count = 0;
35667c478bd9Sstevel@tonic-gate 	np->lease_valid = NFS4_LEASE_NOT_STARTED;
35677c478bd9Sstevel@tonic-gate 	cv_init(&np->s_cv_otw_count, NULL, CV_DEFAULT, NULL);
3568f86c6ccaSdm 	cv_init(&np->s_clientid_pend, NULL, CV_DEFAULT, NULL);
35697c478bd9Sstevel@tonic-gate 	np->s_otw_call_count = 0;
35707c478bd9Sstevel@tonic-gate 	cv_init(&np->wait_cb_null, NULL, CV_DEFAULT, NULL);
35717c478bd9Sstevel@tonic-gate 	np->zoneid = getzoneid();
35727c478bd9Sstevel@tonic-gate 	np->zone_globals = nfs4_get_callback_globals();
35737c478bd9Sstevel@tonic-gate 	ASSERT(np->zone_globals != NULL);
35747c478bd9Sstevel@tonic-gate 	return (np);
35757c478bd9Sstevel@tonic-gate }
35767c478bd9Sstevel@tonic-gate 
35777c478bd9Sstevel@tonic-gate /*
35787c478bd9Sstevel@tonic-gate  * Create a new nfs4_server_t structure and add it to the list.
35797c478bd9Sstevel@tonic-gate  * Returns new node locked; reference must eventually be freed.
35807c478bd9Sstevel@tonic-gate  */
35817c478bd9Sstevel@tonic-gate static struct nfs4_server *
35827c478bd9Sstevel@tonic-gate add_new_nfs4_server(struct servinfo4 *svp, cred_t *cr)
35837c478bd9Sstevel@tonic-gate {
35847c478bd9Sstevel@tonic-gate 	nfs4_server_t *sp;
35857c478bd9Sstevel@tonic-gate 
35867c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&nfs4_server_lst_lock));
35877c478bd9Sstevel@tonic-gate 	sp = new_nfs4_server(svp, cr);
35887c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
35897c478bd9Sstevel@tonic-gate 	insque(sp, &nfs4_server_lst);
35907c478bd9Sstevel@tonic-gate 	sp->s_refcnt++;			/* list gets a reference */
3591f64c4ae1Sdm 	sp->s_flags |= N4S_INSERTED;
35927c478bd9Sstevel@tonic-gate 	sp->clientid = 0;
35937c478bd9Sstevel@tonic-gate 	return (sp);
35947c478bd9Sstevel@tonic-gate }
35957c478bd9Sstevel@tonic-gate 
35967c478bd9Sstevel@tonic-gate int nfs4_server_t_debug = 0;
35977c478bd9Sstevel@tonic-gate 
35987c478bd9Sstevel@tonic-gate #ifdef lint
35997c478bd9Sstevel@tonic-gate extern void
36007c478bd9Sstevel@tonic-gate dumpnfs4slist(char *, mntinfo4_t *, clientid4, servinfo4_t *);
36017c478bd9Sstevel@tonic-gate #endif
36027c478bd9Sstevel@tonic-gate 
36037c478bd9Sstevel@tonic-gate #ifndef lint
36047c478bd9Sstevel@tonic-gate #ifdef DEBUG
36057c478bd9Sstevel@tonic-gate void
36067c478bd9Sstevel@tonic-gate dumpnfs4slist(char *txt, mntinfo4_t *mi, clientid4 clientid, servinfo4_t *srv_p)
36077c478bd9Sstevel@tonic-gate {
36087c478bd9Sstevel@tonic-gate 	int hash16(void *p, int len);
36097c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
36107c478bd9Sstevel@tonic-gate 
36117c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_server_t_debug, (CE_NOTE,
36127c478bd9Sstevel@tonic-gate 	    "dumping nfs4_server_t list in %s", txt));
36137c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
36147c478bd9Sstevel@tonic-gate 	    "mi 0x%p, want clientid %llx, addr %d/%04X",
36157c478bd9Sstevel@tonic-gate 	    mi, (longlong_t)clientid, srv_p->sv_addr.len,
36167c478bd9Sstevel@tonic-gate 	    hash16((void *)srv_p->sv_addr.buf, srv_p->sv_addr.len)));
36177c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst;
36187c478bd9Sstevel@tonic-gate 	    np = np->forw) {
36197c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
36207c478bd9Sstevel@tonic-gate 		    "node 0x%p,    clientid %llx, addr %d/%04X, cnt %d",
36217c478bd9Sstevel@tonic-gate 		    np, (longlong_t)np->clientid, np->saddr.len,
36227c478bd9Sstevel@tonic-gate 		    hash16((void *)np->saddr.buf, np->saddr.len),
36237c478bd9Sstevel@tonic-gate 		    np->state_ref_count));
36247c478bd9Sstevel@tonic-gate 		if (np->saddr.len == srv_p->sv_addr.len &&
36257c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, srv_p->sv_addr.buf,
36267c478bd9Sstevel@tonic-gate 		    np->saddr.len) == 0)
36277c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
36287c478bd9Sstevel@tonic-gate 			    " - address matches"));
36297c478bd9Sstevel@tonic-gate 		if (np->clientid == clientid || np->clientid == 0)
36307c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
36317c478bd9Sstevel@tonic-gate 			    " - clientid matches"));
36327c478bd9Sstevel@tonic-gate 		if (np->s_thread_exit != NFS4_THREAD_EXIT)
36337c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
36347c478bd9Sstevel@tonic-gate 			    " - thread not exiting"));
36357c478bd9Sstevel@tonic-gate 	}
36367c478bd9Sstevel@tonic-gate 	delay(hz);
36377c478bd9Sstevel@tonic-gate }
36387c478bd9Sstevel@tonic-gate #endif
36397c478bd9Sstevel@tonic-gate #endif
36407c478bd9Sstevel@tonic-gate 
36417c478bd9Sstevel@tonic-gate 
36427c478bd9Sstevel@tonic-gate /*
36437c478bd9Sstevel@tonic-gate  * Move a mntinfo4_t from one server list to another.
36447c478bd9Sstevel@tonic-gate  * Locking of the two nfs4_server_t nodes will be done in list order.
36457c478bd9Sstevel@tonic-gate  *
36467c478bd9Sstevel@tonic-gate  * Returns NULL if the current nfs4_server_t for the filesystem could not
36477c478bd9Sstevel@tonic-gate  * be found (e.g., due to forced unmount).  Otherwise returns a reference
36487c478bd9Sstevel@tonic-gate  * to the new nfs4_server_t, which must eventually be freed.
36497c478bd9Sstevel@tonic-gate  */
36507c478bd9Sstevel@tonic-gate nfs4_server_t *
36517c478bd9Sstevel@tonic-gate nfs4_move_mi(mntinfo4_t *mi, servinfo4_t *old, servinfo4_t *new)
36527c478bd9Sstevel@tonic-gate {
36537c478bd9Sstevel@tonic-gate 	nfs4_server_t *p, *op = NULL, *np = NULL;
36547c478bd9Sstevel@tonic-gate 	int num_open;
3655108322fbScarlsonj 	zoneid_t zoneid = nfs_zoneid();
36567c478bd9Sstevel@tonic-gate 
3657108322fbScarlsonj 	ASSERT(nfs_zone() == mi->mi_zone);
36587c478bd9Sstevel@tonic-gate 
36597c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
36607c478bd9Sstevel@tonic-gate #ifdef DEBUG
36617c478bd9Sstevel@tonic-gate 	if (nfs4_server_t_debug)
36627c478bd9Sstevel@tonic-gate 		dumpnfs4slist("nfs4_move_mi", mi, (clientid4)0, new);
36637c478bd9Sstevel@tonic-gate #endif
36647c478bd9Sstevel@tonic-gate 	for (p = nfs4_server_lst.forw; p != &nfs4_server_lst; p = p->forw) {
36657c478bd9Sstevel@tonic-gate 		if (p->zoneid != zoneid)
36667c478bd9Sstevel@tonic-gate 			continue;
36677c478bd9Sstevel@tonic-gate 		if (p->saddr.len == old->sv_addr.len &&
36687c478bd9Sstevel@tonic-gate 		    bcmp(p->saddr.buf, old->sv_addr.buf, p->saddr.len) == 0 &&
36697c478bd9Sstevel@tonic-gate 		    p->s_thread_exit != NFS4_THREAD_EXIT) {
36707c478bd9Sstevel@tonic-gate 			op = p;
36717c478bd9Sstevel@tonic-gate 			mutex_enter(&op->s_lock);
36727c478bd9Sstevel@tonic-gate 			op->s_refcnt++;
36737c478bd9Sstevel@tonic-gate 		}
36747c478bd9Sstevel@tonic-gate 		if (p->saddr.len == new->sv_addr.len &&
36757c478bd9Sstevel@tonic-gate 		    bcmp(p->saddr.buf, new->sv_addr.buf, p->saddr.len) == 0 &&
36767c478bd9Sstevel@tonic-gate 		    p->s_thread_exit != NFS4_THREAD_EXIT) {
36777c478bd9Sstevel@tonic-gate 			np = p;
36787c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
36797c478bd9Sstevel@tonic-gate 		}
36807c478bd9Sstevel@tonic-gate 		if (op != NULL && np != NULL)
36817c478bd9Sstevel@tonic-gate 			break;
36827c478bd9Sstevel@tonic-gate 	}
36837c478bd9Sstevel@tonic-gate 	if (op == NULL) {
36847c478bd9Sstevel@tonic-gate 		/*
36857c478bd9Sstevel@tonic-gate 		 * Filesystem has been forcibly unmounted.  Bail out.
36867c478bd9Sstevel@tonic-gate 		 */
36877c478bd9Sstevel@tonic-gate 		if (np != NULL)
36887c478bd9Sstevel@tonic-gate 			mutex_exit(&np->s_lock);
36897c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_server_lst_lock);
36907c478bd9Sstevel@tonic-gate 		return (NULL);
36917c478bd9Sstevel@tonic-gate 	}
36927c478bd9Sstevel@tonic-gate 	if (np != NULL) {
36937c478bd9Sstevel@tonic-gate 		np->s_refcnt++;
36947c478bd9Sstevel@tonic-gate 	} else {
36957c478bd9Sstevel@tonic-gate #ifdef DEBUG
36967c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
36977c478bd9Sstevel@tonic-gate 		    "nfs4_move_mi: no target nfs4_server, will create."));
36987c478bd9Sstevel@tonic-gate #endif
36997c478bd9Sstevel@tonic-gate 		np = add_new_nfs4_server(new, kcred);
37007c478bd9Sstevel@tonic-gate 	}
37017c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
37027c478bd9Sstevel@tonic-gate 
37037c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
37047c478bd9Sstevel@tonic-gate 	    "nfs4_move_mi: for mi 0x%p, "
37057c478bd9Sstevel@tonic-gate 	    "old servinfo4 0x%p, new servinfo4 0x%p, "
37067c478bd9Sstevel@tonic-gate 	    "old nfs4_server 0x%p, new nfs4_server 0x%p, ",
37077c478bd9Sstevel@tonic-gate 	    (void*)mi, (void*)old, (void*)new,
37087c478bd9Sstevel@tonic-gate 	    (void*)op, (void*)np));
37097c478bd9Sstevel@tonic-gate 	ASSERT(op != NULL && np != NULL);
37107c478bd9Sstevel@tonic-gate 
37117c478bd9Sstevel@tonic-gate 	/* discard any delegations */
37127c478bd9Sstevel@tonic-gate 	nfs4_deleg_discard(mi, op);
37137c478bd9Sstevel@tonic-gate 
37147c478bd9Sstevel@tonic-gate 	num_open = mi->mi_open_files;
37157c478bd9Sstevel@tonic-gate 	mi->mi_open_files = 0;
37167c478bd9Sstevel@tonic-gate 	op->state_ref_count -= num_open;
37177c478bd9Sstevel@tonic-gate 	ASSERT(op->state_ref_count >= 0);
37187c478bd9Sstevel@tonic-gate 	np->state_ref_count += num_open;
37197c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server_nolock(mi, op);
37207c478bd9Sstevel@tonic-gate 	mi->mi_open_files = num_open;
37217c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
37227c478bd9Sstevel@tonic-gate 	    "nfs4_move_mi: mi_open_files %d, op->cnt %d, np->cnt %d",
37237c478bd9Sstevel@tonic-gate 	    mi->mi_open_files, op->state_ref_count, np->state_ref_count));
37247c478bd9Sstevel@tonic-gate 
37257c478bd9Sstevel@tonic-gate 	nfs4_add_mi_to_server(np, mi);
37267c478bd9Sstevel@tonic-gate 
37277c478bd9Sstevel@tonic-gate 	mutex_exit(&op->s_lock);
37287c478bd9Sstevel@tonic-gate 	nfs4_server_rele(op);
37297c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
37307c478bd9Sstevel@tonic-gate 
37317c478bd9Sstevel@tonic-gate 	return (np);
37327c478bd9Sstevel@tonic-gate }
37337c478bd9Sstevel@tonic-gate 
37347c478bd9Sstevel@tonic-gate /*
3735f86c6ccaSdm  * Need to have the nfs4_server_lst_lock.
37367c478bd9Sstevel@tonic-gate  * Search the nfs4_server list to find a match on this servinfo4
37377c478bd9Sstevel@tonic-gate  * based on its address.
37387c478bd9Sstevel@tonic-gate  *
37397c478bd9Sstevel@tonic-gate  * Returns NULL if no match is found.  Otherwise returns a reference (which
37407c478bd9Sstevel@tonic-gate  * must eventually be freed) to a locked nfs4_server.
37417c478bd9Sstevel@tonic-gate  */
37427c478bd9Sstevel@tonic-gate nfs4_server_t *
37437c478bd9Sstevel@tonic-gate servinfo4_to_nfs4_server(servinfo4_t *srv_p)
37447c478bd9Sstevel@tonic-gate {
37457c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
3746108322fbScarlsonj 	zoneid_t zoneid = nfs_zoneid();
37477c478bd9Sstevel@tonic-gate 
3748f86c6ccaSdm 	ASSERT(MUTEX_HELD(&nfs4_server_lst_lock));
37497c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
37507c478bd9Sstevel@tonic-gate 		if (np->zoneid == zoneid &&
37517c478bd9Sstevel@tonic-gate 		    np->saddr.len == srv_p->sv_addr.len &&
37527c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, srv_p->sv_addr.buf,
3753*b9238976Sth 		    np->saddr.len) == 0 &&
37547c478bd9Sstevel@tonic-gate 		    np->s_thread_exit != NFS4_THREAD_EXIT) {
37557c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
37567c478bd9Sstevel@tonic-gate 			np->s_refcnt++;
37577c478bd9Sstevel@tonic-gate 			return (np);
37587c478bd9Sstevel@tonic-gate 		}
37597c478bd9Sstevel@tonic-gate 	}
37607c478bd9Sstevel@tonic-gate 	return (NULL);
37617c478bd9Sstevel@tonic-gate }
37627c478bd9Sstevel@tonic-gate 
37637c478bd9Sstevel@tonic-gate /*
37647c478bd9Sstevel@tonic-gate  * Search the nfs4_server_lst to find a match based on clientid and
37657c478bd9Sstevel@tonic-gate  * addr.
37667c478bd9Sstevel@tonic-gate  * Locks the nfs4_server down if it is found and returns a reference that
37677c478bd9Sstevel@tonic-gate  * must eventually be freed.
37687c478bd9Sstevel@tonic-gate  *
37697c478bd9Sstevel@tonic-gate  * Returns NULL it no match is found.  This means one of two things: either
37707c478bd9Sstevel@tonic-gate  * mi is in the process of being mounted, or mi has been unmounted.
37717c478bd9Sstevel@tonic-gate  *
37727c478bd9Sstevel@tonic-gate  * The caller should be holding mi->mi_recovlock, and it should continue to
37737c478bd9Sstevel@tonic-gate  * hold the lock until done with the returned nfs4_server_t.  Once
37747c478bd9Sstevel@tonic-gate  * mi->mi_recovlock is released, there is no guarantee that the returned
37757c478bd9Sstevel@tonic-gate  * mi->nfs4_server_t will continue to correspond to mi.
37767c478bd9Sstevel@tonic-gate  */
37777c478bd9Sstevel@tonic-gate nfs4_server_t *
37787c478bd9Sstevel@tonic-gate find_nfs4_server(mntinfo4_t *mi)
37797c478bd9Sstevel@tonic-gate {
37807c478bd9Sstevel@tonic-gate 	return (find_nfs4_server_all(mi, 0));
37817c478bd9Sstevel@tonic-gate }
37827c478bd9Sstevel@tonic-gate 
37837c478bd9Sstevel@tonic-gate /*
37847c478bd9Sstevel@tonic-gate  * Same as above, but takes an "all" parameter which can be
37857c478bd9Sstevel@tonic-gate  * set to 1 if the caller wishes to find nfs4_server_t's which
37867c478bd9Sstevel@tonic-gate  * have been marked for termination by the exit of the renew
37877c478bd9Sstevel@tonic-gate  * thread.  This should only be used by operations which are
37887c478bd9Sstevel@tonic-gate  * cleaning up and will not cause an OTW op.
37897c478bd9Sstevel@tonic-gate  */
37907c478bd9Sstevel@tonic-gate nfs4_server_t *
37917c478bd9Sstevel@tonic-gate find_nfs4_server_all(mntinfo4_t *mi, int all)
37927c478bd9Sstevel@tonic-gate {
37937c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
37947c478bd9Sstevel@tonic-gate 	servinfo4_t *svp;
37957c478bd9Sstevel@tonic-gate 	zoneid_t zoneid = mi->mi_zone->zone_id;
37967c478bd9Sstevel@tonic-gate 
37977c478bd9Sstevel@tonic-gate 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
37987c478bd9Sstevel@tonic-gate 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
37997c478bd9Sstevel@tonic-gate 	/*
38007c478bd9Sstevel@tonic-gate 	 * This can be called from nfs4_unmount() which can be called from the
38017c478bd9Sstevel@tonic-gate 	 * global zone, hence it's legal for the global zone to muck with
38027c478bd9Sstevel@tonic-gate 	 * another zone's server list, as long as it doesn't try to contact
38037c478bd9Sstevel@tonic-gate 	 * them.
38047c478bd9Sstevel@tonic-gate 	 */
3805108322fbScarlsonj 	ASSERT(zoneid == getzoneid() || getzoneid() == GLOBAL_ZONEID ||
3806108322fbScarlsonj 	    nfs_global_client_only != 0);
38077c478bd9Sstevel@tonic-gate 
38087c478bd9Sstevel@tonic-gate 	/*
38097c478bd9Sstevel@tonic-gate 	 * The nfs4_server_lst_lock global lock is held when we get a new
38107c478bd9Sstevel@tonic-gate 	 * clientid (via SETCLIENTID OTW).  Holding this global lock and
38117c478bd9Sstevel@tonic-gate 	 * mi_recovlock (READER is fine) ensures that the nfs4_server
38127c478bd9Sstevel@tonic-gate 	 * and this mntinfo4 can't get out of sync, so the following search is
38137c478bd9Sstevel@tonic-gate 	 * always valid.
38147c478bd9Sstevel@tonic-gate 	 */
38157c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
38167c478bd9Sstevel@tonic-gate #ifdef DEBUG
38177c478bd9Sstevel@tonic-gate 	if (nfs4_server_t_debug) {
38187c478bd9Sstevel@tonic-gate 		/* mi->mi_clientid is unprotected, ok for debug output */
38197c478bd9Sstevel@tonic-gate 		dumpnfs4slist("find_nfs4_server", mi, mi->mi_clientid,
3820*b9238976Sth 		    mi->mi_curr_serv);
38217c478bd9Sstevel@tonic-gate 	}
38227c478bd9Sstevel@tonic-gate #endif
38237c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
38247c478bd9Sstevel@tonic-gate 		mutex_enter(&np->s_lock);
38257c478bd9Sstevel@tonic-gate 		svp = mi->mi_curr_serv;
38267c478bd9Sstevel@tonic-gate 
38277c478bd9Sstevel@tonic-gate 		if (np->zoneid == zoneid &&
38287c478bd9Sstevel@tonic-gate 		    np->clientid == mi->mi_clientid &&
38297c478bd9Sstevel@tonic-gate 		    np->saddr.len == svp->sv_addr.len &&
38307c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, svp->sv_addr.buf, np->saddr.len) == 0 &&
38317c478bd9Sstevel@tonic-gate 		    (np->s_thread_exit != NFS4_THREAD_EXIT || all != 0)) {
38327c478bd9Sstevel@tonic-gate 			mutex_exit(&nfs4_server_lst_lock);
38337c478bd9Sstevel@tonic-gate 			np->s_refcnt++;
38347c478bd9Sstevel@tonic-gate 			return (np);
38357c478bd9Sstevel@tonic-gate 		}
38367c478bd9Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
38377c478bd9Sstevel@tonic-gate 	}
38387c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
38397c478bd9Sstevel@tonic-gate 
38407c478bd9Sstevel@tonic-gate 	return (NULL);
38417c478bd9Sstevel@tonic-gate }
38427c478bd9Sstevel@tonic-gate 
38437c478bd9Sstevel@tonic-gate /*
38447c478bd9Sstevel@tonic-gate  * Release the reference to sp and destroy it if that's the last one.
38457c478bd9Sstevel@tonic-gate  */
38467c478bd9Sstevel@tonic-gate 
38477c478bd9Sstevel@tonic-gate void
38487c478bd9Sstevel@tonic-gate nfs4_server_rele(nfs4_server_t *sp)
38497c478bd9Sstevel@tonic-gate {
38507c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
38517c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_refcnt > 0);
38527c478bd9Sstevel@tonic-gate 	sp->s_refcnt--;
38537c478bd9Sstevel@tonic-gate 	if (sp->s_refcnt > 0) {
38547c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
38557c478bd9Sstevel@tonic-gate 		return;
38567c478bd9Sstevel@tonic-gate 	}
38577c478bd9Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
3858f86c6ccaSdm 
38597c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
38607c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
38617c478bd9Sstevel@tonic-gate 	if (sp->s_refcnt > 0) {
38627c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
38637c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_server_lst_lock);
38647c478bd9Sstevel@tonic-gate 		return;
38657c478bd9Sstevel@tonic-gate 	}
3866f86c6ccaSdm 	remque(sp);
3867f86c6ccaSdm 	sp->forw = sp->back = NULL;
38687c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
38697c478bd9Sstevel@tonic-gate 	destroy_nfs4_server(sp);
38707c478bd9Sstevel@tonic-gate }
38717c478bd9Sstevel@tonic-gate 
38727c478bd9Sstevel@tonic-gate static void
38737c478bd9Sstevel@tonic-gate destroy_nfs4_server(nfs4_server_t *sp)
38747c478bd9Sstevel@tonic-gate {
38757c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
38767c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_refcnt == 0);
38777c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_otw_call_count == 0);
38787c478bd9Sstevel@tonic-gate 
38797c478bd9Sstevel@tonic-gate 	remove_all_mi(sp);
38807c478bd9Sstevel@tonic-gate 
38817c478bd9Sstevel@tonic-gate 	crfree(sp->s_cred);
38827c478bd9Sstevel@tonic-gate 	kmem_free(sp->saddr.buf, sp->saddr.maxlen);
38837c478bd9Sstevel@tonic-gate 	kmem_free(sp->clidtosend.id_val, sp->clidtosend.id_len);
38847c478bd9Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
38857c478bd9Sstevel@tonic-gate 
38867c478bd9Sstevel@tonic-gate 	/* destroy the nfs4_server */
38877c478bd9Sstevel@tonic-gate 	nfs4callback_destroy(sp);
38887c478bd9Sstevel@tonic-gate 	list_destroy(&sp->s_deleg_list);
38897c478bd9Sstevel@tonic-gate 	mutex_destroy(&sp->s_lock);
38907c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->cv_thread_exit);
38917c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->s_cv_otw_count);
3892f86c6ccaSdm 	cv_destroy(&sp->s_clientid_pend);
38937c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->wait_cb_null);
38947c478bd9Sstevel@tonic-gate 	nfs_rw_destroy(&sp->s_recovlock);
38957c478bd9Sstevel@tonic-gate 	kmem_free(sp, sizeof (*sp));
38967c478bd9Sstevel@tonic-gate }
38977c478bd9Sstevel@tonic-gate 
38987c478bd9Sstevel@tonic-gate /*
38997c478bd9Sstevel@tonic-gate  * Lock sp, but only if it's still active (in the list and hasn't been
39007c478bd9Sstevel@tonic-gate  * flagged as exiting) or 'all' is non-zero.
39017c478bd9Sstevel@tonic-gate  * Returns TRUE if sp got locked and adds a reference to sp.
39027c478bd9Sstevel@tonic-gate  */
39037c478bd9Sstevel@tonic-gate bool_t
39047c478bd9Sstevel@tonic-gate nfs4_server_vlock(nfs4_server_t *sp, int all)
39057c478bd9Sstevel@tonic-gate {
39067c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
39077c478bd9Sstevel@tonic-gate 
39087c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
39097c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
39107c478bd9Sstevel@tonic-gate 		if (sp == np && (np->s_thread_exit != NFS4_THREAD_EXIT ||
39117c478bd9Sstevel@tonic-gate 		    all != 0)) {
39127c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
39137c478bd9Sstevel@tonic-gate 			np->s_refcnt++;
39147c478bd9Sstevel@tonic-gate 			mutex_exit(&nfs4_server_lst_lock);
39157c478bd9Sstevel@tonic-gate 			return (TRUE);
39167c478bd9Sstevel@tonic-gate 		}
39177c478bd9Sstevel@tonic-gate 	}
39187c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
39197c478bd9Sstevel@tonic-gate 	return (FALSE);
39207c478bd9Sstevel@tonic-gate }
39217c478bd9Sstevel@tonic-gate 
39227c478bd9Sstevel@tonic-gate /*
39237c478bd9Sstevel@tonic-gate  * Fork off a thread to free the data structures for a mount.
39247c478bd9Sstevel@tonic-gate  */
39257c478bd9Sstevel@tonic-gate 
39267c478bd9Sstevel@tonic-gate static void
3927*b9238976Sth async_free_mount(vfs_t *vfsp, int flag, cred_t *cr)
39287c478bd9Sstevel@tonic-gate {
39297c478bd9Sstevel@tonic-gate 	freemountargs_t *args;
39307c478bd9Sstevel@tonic-gate 	args = kmem_alloc(sizeof (freemountargs_t), KM_SLEEP);
39317c478bd9Sstevel@tonic-gate 	args->fm_vfsp = vfsp;
39327c478bd9Sstevel@tonic-gate 	VFS_HOLD(vfsp);
393350a83466Sjwahlig 	MI4_HOLD(VFTOMI4(vfsp));
3934*b9238976Sth 	args->fm_flag = flag;
39357c478bd9Sstevel@tonic-gate 	args->fm_cr = cr;
39367c478bd9Sstevel@tonic-gate 	crhold(cr);
39377c478bd9Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, nfs4_free_mount_thread, args, 0,
39387c478bd9Sstevel@tonic-gate 	    minclsyspri);
39397c478bd9Sstevel@tonic-gate }
39407c478bd9Sstevel@tonic-gate 
39417c478bd9Sstevel@tonic-gate static void
39427c478bd9Sstevel@tonic-gate nfs4_free_mount_thread(freemountargs_t *args)
39437c478bd9Sstevel@tonic-gate {
394450a83466Sjwahlig 	mntinfo4_t *mi;
3945*b9238976Sth 	nfs4_free_mount(args->fm_vfsp, args->fm_flag, args->fm_cr);
394650a83466Sjwahlig 	mi = VFTOMI4(args->fm_vfsp);
39477c478bd9Sstevel@tonic-gate 	crfree(args->fm_cr);
394850a83466Sjwahlig 	VFS_RELE(args->fm_vfsp);
394950a83466Sjwahlig 	MI4_RELE(mi);
39507c478bd9Sstevel@tonic-gate 	kmem_free(args, sizeof (freemountargs_t));
39517c478bd9Sstevel@tonic-gate 	zthread_exit();
39527c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
39537c478bd9Sstevel@tonic-gate }
39547c478bd9Sstevel@tonic-gate 
39557c478bd9Sstevel@tonic-gate /*
39567c478bd9Sstevel@tonic-gate  * Thread to free the data structures for a given filesystem.
39577c478bd9Sstevel@tonic-gate  */
39587c478bd9Sstevel@tonic-gate static void
3959*b9238976Sth nfs4_free_mount(vfs_t *vfsp, int flag, cred_t *cr)
39607c478bd9Sstevel@tonic-gate {
3961*b9238976Sth 	mntinfo4_t		*mi = VFTOMI4(vfsp);
3962*b9238976Sth 	nfs4_server_t		*sp;
3963*b9238976Sth 	callb_cpr_t		cpr_info;
3964*b9238976Sth 	kmutex_t		cpr_lock;
3965*b9238976Sth 	boolean_t		async_thread;
3966*b9238976Sth 	int			removed;
3967*b9238976Sth 
3968*b9238976Sth 	bool_t			must_unlock = FALSE;
3969*b9238976Sth 	nfs4_ephemeral_tree_t	*eph_tree;
39707c478bd9Sstevel@tonic-gate 
39717c478bd9Sstevel@tonic-gate 	/*
39727c478bd9Sstevel@tonic-gate 	 * We need to participate in the CPR framework if this is a kernel
39737c478bd9Sstevel@tonic-gate 	 * thread.
39747c478bd9Sstevel@tonic-gate 	 */
3975108322fbScarlsonj 	async_thread = (curproc == nfs_zone()->zone_zsched);
39767c478bd9Sstevel@tonic-gate 	if (async_thread) {
39777c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL);
39787c478bd9Sstevel@tonic-gate 		CALLB_CPR_INIT(&cpr_info, &cpr_lock, callb_generic_cpr,
39797c478bd9Sstevel@tonic-gate 		    "nfsv4AsyncUnmount");
39807c478bd9Sstevel@tonic-gate 	}
39817c478bd9Sstevel@tonic-gate 
39827c478bd9Sstevel@tonic-gate 	/*
39837c478bd9Sstevel@tonic-gate 	 * We need to wait for all outstanding OTW calls
39847c478bd9Sstevel@tonic-gate 	 * and recovery to finish before we remove the mi
39857c478bd9Sstevel@tonic-gate 	 * from the nfs4_server_t, as current pending
39867c478bd9Sstevel@tonic-gate 	 * calls might still need this linkage (in order
39877c478bd9Sstevel@tonic-gate 	 * to find a nfs4_server_t from a mntinfo4_t).
39887c478bd9Sstevel@tonic-gate 	 */
39897c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, FALSE);
39907c478bd9Sstevel@tonic-gate 	sp = find_nfs4_server(mi);
39917c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&mi->mi_recovlock);
39927c478bd9Sstevel@tonic-gate 
39937c478bd9Sstevel@tonic-gate 	if (sp) {
39947c478bd9Sstevel@tonic-gate 		while (sp->s_otw_call_count != 0) {
39957c478bd9Sstevel@tonic-gate 			if (async_thread) {
39967c478bd9Sstevel@tonic-gate 				mutex_enter(&cpr_lock);
39977c478bd9Sstevel@tonic-gate 				CALLB_CPR_SAFE_BEGIN(&cpr_info);
39987c478bd9Sstevel@tonic-gate 				mutex_exit(&cpr_lock);
39997c478bd9Sstevel@tonic-gate 			}
40007c478bd9Sstevel@tonic-gate 			cv_wait(&sp->s_cv_otw_count, &sp->s_lock);
40017c478bd9Sstevel@tonic-gate 			if (async_thread) {
40027c478bd9Sstevel@tonic-gate 				mutex_enter(&cpr_lock);
40037c478bd9Sstevel@tonic-gate 				CALLB_CPR_SAFE_END(&cpr_info, &cpr_lock);
40047c478bd9Sstevel@tonic-gate 				mutex_exit(&cpr_lock);
40057c478bd9Sstevel@tonic-gate 			}
40067c478bd9Sstevel@tonic-gate 		}
40077c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
40087c478bd9Sstevel@tonic-gate 		nfs4_server_rele(sp);
40097c478bd9Sstevel@tonic-gate 		sp = NULL;
40107c478bd9Sstevel@tonic-gate 	}
40117c478bd9Sstevel@tonic-gate 
40127c478bd9Sstevel@tonic-gate 
40137c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
40147c478bd9Sstevel@tonic-gate 	while (mi->mi_in_recovery != 0) {
40157c478bd9Sstevel@tonic-gate 		if (async_thread) {
40167c478bd9Sstevel@tonic-gate 			mutex_enter(&cpr_lock);
40177c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_BEGIN(&cpr_info);
40187c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_lock);
40197c478bd9Sstevel@tonic-gate 		}
40207c478bd9Sstevel@tonic-gate 		cv_wait(&mi->mi_cv_in_recov, &mi->mi_lock);
40217c478bd9Sstevel@tonic-gate 		if (async_thread) {
40227c478bd9Sstevel@tonic-gate 			mutex_enter(&cpr_lock);
40237c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cpr_info, &cpr_lock);
40247c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_lock);
40257c478bd9Sstevel@tonic-gate 		}
40267c478bd9Sstevel@tonic-gate 	}
40277c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
40287c478bd9Sstevel@tonic-gate 
4029*b9238976Sth 	(void) nfs4_ephemeral_umount(mi, flag, cr,
4030*b9238976Sth 	    &must_unlock, &eph_tree);
4031*b9238976Sth 	nfs4_ephemeral_umount_activate(mi, &must_unlock, &eph_tree);
4032*b9238976Sth 
40337c478bd9Sstevel@tonic-gate 	/*
40347c478bd9Sstevel@tonic-gate 	 * The original purge of the dnlc via 'dounmount'
40357c478bd9Sstevel@tonic-gate 	 * doesn't guarantee that another dnlc entry was not
40367c478bd9Sstevel@tonic-gate 	 * added while we waitied for all outstanding OTW
40377c478bd9Sstevel@tonic-gate 	 * and recovery calls to finish.  So re-purge the
40387c478bd9Sstevel@tonic-gate 	 * dnlc now.
40397c478bd9Sstevel@tonic-gate 	 */
40407c478bd9Sstevel@tonic-gate 	(void) dnlc_purge_vfsp(vfsp, 0);
40417c478bd9Sstevel@tonic-gate 
40427c478bd9Sstevel@tonic-gate 	/*
40437c478bd9Sstevel@tonic-gate 	 * We need to explicitly stop the manager thread; the asyc worker
40447c478bd9Sstevel@tonic-gate 	 * threads can timeout and exit on their own.
40457c478bd9Sstevel@tonic-gate 	 */
404650a83466Sjwahlig 	mutex_enter(&mi->mi_async_lock);
404750a83466Sjwahlig 	mi->mi_max_threads = 0;
404850a83466Sjwahlig 	cv_broadcast(&mi->mi_async_work_cv);
404950a83466Sjwahlig 	mutex_exit(&mi->mi_async_lock);
405050a83466Sjwahlig 	if (mi->mi_manager_thread)
405150a83466Sjwahlig 		nfs4_async_manager_stop(vfsp);
40527c478bd9Sstevel@tonic-gate 
40537c478bd9Sstevel@tonic-gate 	destroy_rtable4(vfsp, cr);
40547c478bd9Sstevel@tonic-gate 
40557c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server(mi, NULL);
40567c478bd9Sstevel@tonic-gate 
40577c478bd9Sstevel@tonic-gate 	if (async_thread) {
40587c478bd9Sstevel@tonic-gate 		mutex_enter(&cpr_lock);
40597c478bd9Sstevel@tonic-gate 		CALLB_CPR_EXIT(&cpr_info);	/* drops cpr_lock */
40607c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_lock);
40617c478bd9Sstevel@tonic-gate 	}
406250a83466Sjwahlig 
406350a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
406450a83466Sjwahlig 	if (removed)
406550a83466Sjwahlig 		zone_rele(mi->mi_zone);
40667c478bd9Sstevel@tonic-gate }
4067