xref: /titanic_41/usr/src/uts/common/fs/nfs/nfs4_vfsops.c (revision 92034044e95e6f6e8fb6a3dddf68ddc2561c6870)
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
5f86c6ccaSdm120769  * Common Development and Distribution License (the "License").
6f86c6ccaSdm120769  * 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  */
21bb5aeaf5SMarcel Telka 
227c478bd9Sstevel@tonic-gate /*
23bb5aeaf5SMarcel Telka  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24d7de0ceaSRobert Harris  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  *	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
297c478bd9Sstevel@tonic-gate  *	All Rights Reserved
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <sys/param.h>
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/systm.h>
357c478bd9Sstevel@tonic-gate #include <sys/cred.h>
367c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
37aa59c4cbSrsb #include <sys/vfs_opreg.h>
387c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
397c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
407c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
417c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
427c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
437c478bd9Sstevel@tonic-gate #include <sys/mount.h>
447c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
457c478bd9Sstevel@tonic-gate #include <sys/errno.h>
467c478bd9Sstevel@tonic-gate #include <sys/debug.h>
477c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
487c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
497c478bd9Sstevel@tonic-gate #include <sys/bootconf.h>
507c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
517c478bd9Sstevel@tonic-gate #include <sys/acl.h>
527c478bd9Sstevel@tonic-gate #include <sys/flock.h>
537c478bd9Sstevel@tonic-gate #include <sys/time.h>
547c478bd9Sstevel@tonic-gate #include <sys/disp.h>
557c478bd9Sstevel@tonic-gate #include <sys/policy.h>
567c478bd9Sstevel@tonic-gate #include <sys/socket.h>
577c478bd9Sstevel@tonic-gate #include <sys/netconfig.h>
587c478bd9Sstevel@tonic-gate #include <sys/dnlc.h>
597c478bd9Sstevel@tonic-gate #include <sys/list.h>
6045916cd2Sjpk #include <sys/mntent.h>
6145916cd2Sjpk #include <sys/tsol/label.h>
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate #include <rpc/types.h>
647c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
657c478bd9Sstevel@tonic-gate #include <rpc/rpcsec_gss.h>
667c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
697c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
707c478bd9Sstevel@tonic-gate #include <nfs/mount.h>
717c478bd9Sstevel@tonic-gate #include <nfs/nfs_acl.h>
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate #include <nfs/nfs4.h>
767c478bd9Sstevel@tonic-gate #include <nfs/rnode4.h>
777c478bd9Sstevel@tonic-gate #include <nfs/nfs4_clnt.h>
7839d3e169Sevanl #include <sys/fs/autofs.h>
7939d3e169Sevanl 
802f172c55SRobert Thurlow #include <sys/sdt.h>
812f172c55SRobert Thurlow 
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate /*
847c478bd9Sstevel@tonic-gate  * Arguments passed to thread to free data structures from forced unmount.
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate typedef struct {
887c478bd9Sstevel@tonic-gate 	vfs_t	*fm_vfsp;
89b9238976Sth199096 	int	fm_flag;
907c478bd9Sstevel@tonic-gate 	cred_t	*fm_cr;
917c478bd9Sstevel@tonic-gate } freemountargs_t;
927c478bd9Sstevel@tonic-gate 
93b9238976Sth199096 static void	async_free_mount(vfs_t *, int, cred_t *);
94b9238976Sth199096 static void	nfs4_free_mount(vfs_t *, int, cred_t *);
957c478bd9Sstevel@tonic-gate static void	nfs4_free_mount_thread(freemountargs_t *);
967c478bd9Sstevel@tonic-gate static int nfs4_chkdup_servinfo4(servinfo4_t *, servinfo4_t *);
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate /*
997c478bd9Sstevel@tonic-gate  * From rpcsec module (common/rpcsec).
1007c478bd9Sstevel@tonic-gate  */
1017c478bd9Sstevel@tonic-gate extern int sec_clnt_loadinfo(struct sec_data *, struct sec_data **, model_t);
1027c478bd9Sstevel@tonic-gate extern void sec_clnt_freeinfo(struct sec_data *);
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate  * The order and contents of this structure must be kept in sync with that of
1067c478bd9Sstevel@tonic-gate  * rfsreqcnt_v4_tmpl in nfs_stats.c
1077c478bd9Sstevel@tonic-gate  */
1087c478bd9Sstevel@tonic-gate static char *rfsnames_v4[] = {
1097c478bd9Sstevel@tonic-gate 	"null", "compound", "reserved",	"access", "close", "commit", "create",
1107c478bd9Sstevel@tonic-gate 	"delegpurge", "delegreturn", "getattr",	"getfh", "link", "lock",
1117c478bd9Sstevel@tonic-gate 	"lockt", "locku", "lookup", "lookupp", "nverify", "open", "openattr",
1127c478bd9Sstevel@tonic-gate 	"open_confirm",	"open_downgrade", "putfh", "putpubfh", "putrootfh",
1137c478bd9Sstevel@tonic-gate 	"read", "readdir", "readlink", "remove", "rename", "renew",
1147c478bd9Sstevel@tonic-gate 	"restorefh", "savefh", "secinfo", "setattr", "setclientid",
1157c478bd9Sstevel@tonic-gate 	"setclientid_confirm", "verify", "write"
1167c478bd9Sstevel@tonic-gate };
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate /*
1197c478bd9Sstevel@tonic-gate  * nfs4_max_mount_retry is the number of times the client will redrive
1207c478bd9Sstevel@tonic-gate  * a mount compound before giving up and returning failure.  The intent
1217c478bd9Sstevel@tonic-gate  * is to redrive mount compounds which fail NFS4ERR_STALE so that
1227c478bd9Sstevel@tonic-gate  * if a component of the server path being mounted goes stale, it can
1237c478bd9Sstevel@tonic-gate  * "recover" by redriving the mount compund (LOOKUP ops).  This recovery
1247c478bd9Sstevel@tonic-gate  * code is needed outside of the recovery framework because mount is a
1257c478bd9Sstevel@tonic-gate  * special case.  The client doesn't create vnodes/rnodes for components
1267c478bd9Sstevel@tonic-gate  * of the server path being mounted.  The recovery code recovers real
1277c478bd9Sstevel@tonic-gate  * client objects, not STALE FHs which map to components of the server
1287c478bd9Sstevel@tonic-gate  * path being mounted.
1297c478bd9Sstevel@tonic-gate  *
1307c478bd9Sstevel@tonic-gate  * We could just fail the mount on the first time, but that would
1317c478bd9Sstevel@tonic-gate  * instantly trigger failover (from nfs4_mount), and the client should
1327c478bd9Sstevel@tonic-gate  * try to re-lookup the STALE FH before doing failover.  The easiest
1337c478bd9Sstevel@tonic-gate  * way to "re-lookup" is to simply redrive the mount compound.
1347c478bd9Sstevel@tonic-gate  */
1357c478bd9Sstevel@tonic-gate static int nfs4_max_mount_retry = 2;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate /*
1387c478bd9Sstevel@tonic-gate  * nfs4 vfs operations.
1397c478bd9Sstevel@tonic-gate  */
140b9238976Sth199096 int		nfs4_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
1417c478bd9Sstevel@tonic-gate static int	nfs4_unmount(vfs_t *, int, cred_t *);
1427c478bd9Sstevel@tonic-gate static int	nfs4_root(vfs_t *, vnode_t **);
1437c478bd9Sstevel@tonic-gate static int	nfs4_statvfs(vfs_t *, struct statvfs64 *);
1447c478bd9Sstevel@tonic-gate static int	nfs4_sync(vfs_t *, short, cred_t *);
1457c478bd9Sstevel@tonic-gate static int	nfs4_vget(vfs_t *, vnode_t **, fid_t *);
1467c478bd9Sstevel@tonic-gate static int	nfs4_mountroot(vfs_t *, whymountroot_t);
1477c478bd9Sstevel@tonic-gate static void	nfs4_freevfs(vfs_t *);
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate static int	nfs4rootvp(vnode_t **, vfs_t *, struct servinfo4 *,
1507c478bd9Sstevel@tonic-gate 		    int, cred_t *, zone_t *);
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate vfsops_t	*nfs4_vfsops;
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate int nfs4_vfsinit(void);
1557c478bd9Sstevel@tonic-gate void nfs4_vfsfini(void);
1567c478bd9Sstevel@tonic-gate static void nfs4setclientid_init(void);
1577c478bd9Sstevel@tonic-gate static void nfs4setclientid_fini(void);
1587c478bd9Sstevel@tonic-gate static void nfs4setclientid_otw(mntinfo4_t *, servinfo4_t *,  cred_t *,
1597c478bd9Sstevel@tonic-gate 		struct nfs4_server *, nfs4_error_t *, int *);
1607c478bd9Sstevel@tonic-gate static void	destroy_nfs4_server(nfs4_server_t *);
1617c478bd9Sstevel@tonic-gate static void	remove_mi(nfs4_server_t *, mntinfo4_t *);
1627c478bd9Sstevel@tonic-gate 
163b9238976Sth199096 extern void nfs4_ephemeral_init(void);
164b9238976Sth199096 extern void nfs4_ephemeral_fini(void);
165b9238976Sth199096 
1662f172c55SRobert Thurlow /* referral related routines */
1672f172c55SRobert Thurlow static servinfo4_t *copy_svp(servinfo4_t *);
1682f172c55SRobert Thurlow static void free_knconf_contents(struct knetconfig *k);
1692f172c55SRobert Thurlow static char *extract_referral_point(const char *, int);
1702f172c55SRobert Thurlow static void setup_newsvpath(servinfo4_t *, int);
1712f172c55SRobert Thurlow static void update_servinfo4(servinfo4_t *, fs_location4 *,
1722f172c55SRobert Thurlow 		struct nfs_fsl_info *, char *, int);
1732f172c55SRobert Thurlow 
1747c478bd9Sstevel@tonic-gate /*
1757c478bd9Sstevel@tonic-gate  * Initialize the vfs structure
1767c478bd9Sstevel@tonic-gate  */
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate static int nfs4fstyp;
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate /*
1827c478bd9Sstevel@tonic-gate  * Debug variable to check for rdma based
1837c478bd9Sstevel@tonic-gate  * transport startup and cleanup. Controlled
1847c478bd9Sstevel@tonic-gate  * through /etc/system. Off by default.
1857c478bd9Sstevel@tonic-gate  */
1867c478bd9Sstevel@tonic-gate extern int rdma_debug;
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate int
nfs4init(int fstyp,char * name)1897c478bd9Sstevel@tonic-gate nfs4init(int fstyp, char *name)
1907c478bd9Sstevel@tonic-gate {
1917c478bd9Sstevel@tonic-gate 	static const fs_operation_def_t nfs4_vfsops_template[] = {
192aa59c4cbSrsb 		VFSNAME_MOUNT,		{ .vfs_mount = nfs4_mount },
193aa59c4cbSrsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = nfs4_unmount },
194aa59c4cbSrsb 		VFSNAME_ROOT,		{ .vfs_root = nfs4_root },
195aa59c4cbSrsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = nfs4_statvfs },
196aa59c4cbSrsb 		VFSNAME_SYNC,		{ .vfs_sync = nfs4_sync },
197aa59c4cbSrsb 		VFSNAME_VGET,		{ .vfs_vget = nfs4_vget },
198aa59c4cbSrsb 		VFSNAME_MOUNTROOT,	{ .vfs_mountroot = nfs4_mountroot },
199aa59c4cbSrsb 		VFSNAME_FREEVFS,	{ .vfs_freevfs = nfs4_freevfs },
2007c478bd9Sstevel@tonic-gate 		NULL,			NULL
2017c478bd9Sstevel@tonic-gate 	};
2027c478bd9Sstevel@tonic-gate 	int error;
2037c478bd9Sstevel@tonic-gate 
204b9238976Sth199096 	nfs4_vfsops = NULL;
205b9238976Sth199096 	nfs4_vnodeops = NULL;
206b9238976Sth199096 	nfs4_trigger_vnodeops = NULL;
207b9238976Sth199096 
2087c478bd9Sstevel@tonic-gate 	error = vfs_setfsops(fstyp, nfs4_vfsops_template, &nfs4_vfsops);
2097c478bd9Sstevel@tonic-gate 	if (error != 0) {
2107c478bd9Sstevel@tonic-gate 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
2117c478bd9Sstevel@tonic-gate 		    "nfs4init: bad vfs ops template");
212b9238976Sth199096 		goto out;
2137c478bd9Sstevel@tonic-gate 	}
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	error = vn_make_ops(name, nfs4_vnodeops_template, &nfs4_vnodeops);
2167c478bd9Sstevel@tonic-gate 	if (error != 0) {
2177c478bd9Sstevel@tonic-gate 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
2187c478bd9Sstevel@tonic-gate 		    "nfs4init: bad vnode ops template");
219b9238976Sth199096 		goto out;
220b9238976Sth199096 	}
221b9238976Sth199096 
222b9238976Sth199096 	error = vn_make_ops("nfs4_trigger", nfs4_trigger_vnodeops_template,
223b9238976Sth199096 	    &nfs4_trigger_vnodeops);
224b9238976Sth199096 	if (error != 0) {
225b9238976Sth199096 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
226b9238976Sth199096 		    "nfs4init: bad trigger vnode ops template");
227b9238976Sth199096 		goto out;
2287c478bd9Sstevel@tonic-gate 	}
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	nfs4fstyp = fstyp;
2317c478bd9Sstevel@tonic-gate 	(void) nfs4_vfsinit();
2327c478bd9Sstevel@tonic-gate 	(void) nfs4_init_dot_entries();
2337c478bd9Sstevel@tonic-gate 
234b9238976Sth199096 out:
235b9238976Sth199096 	if (error) {
236b9238976Sth199096 		if (nfs4_trigger_vnodeops != NULL)
237b9238976Sth199096 			vn_freevnodeops(nfs4_trigger_vnodeops);
238b9238976Sth199096 
239b9238976Sth199096 		if (nfs4_vnodeops != NULL)
240b9238976Sth199096 			vn_freevnodeops(nfs4_vnodeops);
241b9238976Sth199096 
242b9238976Sth199096 		(void) vfs_freevfsops_by_type(fstyp);
243b9238976Sth199096 	}
244b9238976Sth199096 
245b9238976Sth199096 	return (error);
2467c478bd9Sstevel@tonic-gate }
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate void
nfs4fini(void)2497c478bd9Sstevel@tonic-gate nfs4fini(void)
2507c478bd9Sstevel@tonic-gate {
2517c478bd9Sstevel@tonic-gate 	(void) nfs4_destroy_dot_entries();
2527c478bd9Sstevel@tonic-gate 	nfs4_vfsfini();
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate /*
2567c478bd9Sstevel@tonic-gate  * Create a new sec_data structure to store AUTH_DH related data:
2577c478bd9Sstevel@tonic-gate  * netname, syncaddr, knetconfig. There is no AUTH_F_RPCTIMESYNC
2587c478bd9Sstevel@tonic-gate  * flag set for NFS V4 since we are avoiding to contact the rpcbind
2597c478bd9Sstevel@tonic-gate  * daemon and is using the IP time service (IPPORT_TIMESERVER).
2607c478bd9Sstevel@tonic-gate  *
2617c478bd9Sstevel@tonic-gate  * sec_data can be freed by sec_clnt_freeinfo().
2627c478bd9Sstevel@tonic-gate  */
263b9238976Sth199096 static struct sec_data *
create_authdh_data(char * netname,int nlen,struct netbuf * syncaddr,struct knetconfig * knconf)2647c478bd9Sstevel@tonic-gate create_authdh_data(char *netname, int nlen, struct netbuf *syncaddr,
2657c478bd9Sstevel@tonic-gate 		struct knetconfig *knconf) {
2667c478bd9Sstevel@tonic-gate 	struct sec_data *secdata;
2677c478bd9Sstevel@tonic-gate 	dh_k4_clntdata_t *data;
2687c478bd9Sstevel@tonic-gate 	char *pf, *p;
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	if (syncaddr == NULL || syncaddr->buf == NULL || nlen == 0)
2717c478bd9Sstevel@tonic-gate 		return (NULL);
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
2747c478bd9Sstevel@tonic-gate 	secdata->flags = 0;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	data = kmem_alloc(sizeof (*data), KM_SLEEP);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	data->syncaddr.maxlen = syncaddr->maxlen;
2797c478bd9Sstevel@tonic-gate 	data->syncaddr.len = syncaddr->len;
2807c478bd9Sstevel@tonic-gate 	data->syncaddr.buf = (char *)kmem_alloc(syncaddr->len, KM_SLEEP);
2817c478bd9Sstevel@tonic-gate 	bcopy(syncaddr->buf, data->syncaddr.buf, syncaddr->len);
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	/*
2847c478bd9Sstevel@tonic-gate 	 * duplicate the knconf information for the
2857c478bd9Sstevel@tonic-gate 	 * new opaque data.
2867c478bd9Sstevel@tonic-gate 	 */
2877c478bd9Sstevel@tonic-gate 	data->knconf = kmem_alloc(sizeof (*knconf), KM_SLEEP);
2887c478bd9Sstevel@tonic-gate 	*data->knconf = *knconf;
2897c478bd9Sstevel@tonic-gate 	pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
2907c478bd9Sstevel@tonic-gate 	p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
2917c478bd9Sstevel@tonic-gate 	bcopy(knconf->knc_protofmly, pf, KNC_STRSIZE);
2927c478bd9Sstevel@tonic-gate 	bcopy(knconf->knc_proto, p, KNC_STRSIZE);
2937c478bd9Sstevel@tonic-gate 	data->knconf->knc_protofmly = pf;
2947c478bd9Sstevel@tonic-gate 	data->knconf->knc_proto = p;
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	/* move server netname to the sec_data structure */
2977c478bd9Sstevel@tonic-gate 	data->netname = kmem_alloc(nlen, KM_SLEEP);
2987c478bd9Sstevel@tonic-gate 	bcopy(netname, data->netname, nlen);
2997c478bd9Sstevel@tonic-gate 	data->netnamelen = (int)nlen;
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 	secdata->secmod = AUTH_DH;
3027c478bd9Sstevel@tonic-gate 	secdata->rpcflavor = AUTH_DH;
3037c478bd9Sstevel@tonic-gate 	secdata->data = (caddr_t)data;
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	return (secdata);
3067c478bd9Sstevel@tonic-gate }
3077c478bd9Sstevel@tonic-gate 
308b9238976Sth199096 /*
309b9238976Sth199096  * Returns (deep) copy of sec_data_t. Allocates all memory required; caller
310b9238976Sth199096  * is responsible for freeing.
311b9238976Sth199096  */
312b9238976Sth199096 sec_data_t *
copy_sec_data(sec_data_t * fsecdata)313b9238976Sth199096 copy_sec_data(sec_data_t *fsecdata) {
314b9238976Sth199096 	sec_data_t *tsecdata;
315b9238976Sth199096 
316b9238976Sth199096 	if (fsecdata == NULL)
317b9238976Sth199096 		return (NULL);
318b9238976Sth199096 
319b9238976Sth199096 	if (fsecdata->rpcflavor == AUTH_DH) {
320b9238976Sth199096 		dh_k4_clntdata_t *fdata = (dh_k4_clntdata_t *)fsecdata->data;
321b9238976Sth199096 
322b9238976Sth199096 		if (fdata == NULL)
323b9238976Sth199096 			return (NULL);
324b9238976Sth199096 
325b9238976Sth199096 		tsecdata = (sec_data_t *)create_authdh_data(fdata->netname,
326b9238976Sth199096 		    fdata->netnamelen, &fdata->syncaddr, fdata->knconf);
327b9238976Sth199096 
328b9238976Sth199096 		return (tsecdata);
329b9238976Sth199096 	}
330b9238976Sth199096 
331b9238976Sth199096 	tsecdata = kmem_zalloc(sizeof (sec_data_t), KM_SLEEP);
332b9238976Sth199096 
333b9238976Sth199096 	tsecdata->secmod = fsecdata->secmod;
334b9238976Sth199096 	tsecdata->rpcflavor = fsecdata->rpcflavor;
335b9238976Sth199096 	tsecdata->flags = fsecdata->flags;
336b9238976Sth199096 	tsecdata->uid = fsecdata->uid;
337b9238976Sth199096 
338b9238976Sth199096 	if (fsecdata->rpcflavor == RPCSEC_GSS) {
339b9238976Sth199096 		gss_clntdata_t *gcd = (gss_clntdata_t *)fsecdata->data;
340b9238976Sth199096 
341b9238976Sth199096 		tsecdata->data = (caddr_t)copy_sec_data_gss(gcd);
342b9238976Sth199096 	} else {
343b9238976Sth199096 		tsecdata->data = NULL;
344b9238976Sth199096 	}
345b9238976Sth199096 
346b9238976Sth199096 	return (tsecdata);
347b9238976Sth199096 }
348b9238976Sth199096 
349b9238976Sth199096 gss_clntdata_t *
copy_sec_data_gss(gss_clntdata_t * fdata)350b9238976Sth199096 copy_sec_data_gss(gss_clntdata_t *fdata)
351b9238976Sth199096 {
352b9238976Sth199096 	gss_clntdata_t *tdata;
353b9238976Sth199096 
354b9238976Sth199096 	if (fdata == NULL)
355b9238976Sth199096 		return (NULL);
356b9238976Sth199096 
357b9238976Sth199096 	tdata = kmem_zalloc(sizeof (gss_clntdata_t), KM_SLEEP);
358b9238976Sth199096 
359b9238976Sth199096 	tdata->mechanism.length = fdata->mechanism.length;
360b9238976Sth199096 	tdata->mechanism.elements = kmem_zalloc(fdata->mechanism.length,
361b9238976Sth199096 	    KM_SLEEP);
362b9238976Sth199096 	bcopy(fdata->mechanism.elements, tdata->mechanism.elements,
363b9238976Sth199096 	    fdata->mechanism.length);
364b9238976Sth199096 
365b9238976Sth199096 	tdata->service = fdata->service;
366b9238976Sth199096 
367b9238976Sth199096 	(void) strcpy(tdata->uname, fdata->uname);
368b9238976Sth199096 	(void) strcpy(tdata->inst, fdata->inst);
369b9238976Sth199096 	(void) strcpy(tdata->realm, fdata->realm);
370b9238976Sth199096 
371b9238976Sth199096 	tdata->qop = fdata->qop;
372b9238976Sth199096 
373b9238976Sth199096 	return (tdata);
374b9238976Sth199096 }
375b9238976Sth199096 
3767c478bd9Sstevel@tonic-gate static int
nfs4_chkdup_servinfo4(servinfo4_t * svp_head,servinfo4_t * svp)3777c478bd9Sstevel@tonic-gate nfs4_chkdup_servinfo4(servinfo4_t *svp_head, servinfo4_t *svp)
3787c478bd9Sstevel@tonic-gate {
3797c478bd9Sstevel@tonic-gate 	servinfo4_t *si;
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	/*
3827c478bd9Sstevel@tonic-gate 	 * Iterate over the servinfo4 list to make sure
3837c478bd9Sstevel@tonic-gate 	 * we do not have a duplicate. Skip any servinfo4
3847c478bd9Sstevel@tonic-gate 	 * that has been marked "NOT IN USE"
3857c478bd9Sstevel@tonic-gate 	 */
3867c478bd9Sstevel@tonic-gate 	for (si = svp_head; si; si = si->sv_next) {
3877c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&si->sv_lock, RW_READER, 0);
3887c478bd9Sstevel@tonic-gate 		if (si->sv_flags & SV4_NOTINUSE) {
3897c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&si->sv_lock);
3907c478bd9Sstevel@tonic-gate 			continue;
3917c478bd9Sstevel@tonic-gate 		}
3927c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&si->sv_lock);
3937c478bd9Sstevel@tonic-gate 		if (si == svp)
3947c478bd9Sstevel@tonic-gate 			continue;
3957c478bd9Sstevel@tonic-gate 		if (si->sv_addr.len == svp->sv_addr.len &&
3967c478bd9Sstevel@tonic-gate 		    strcmp(si->sv_knconf->knc_protofmly,
3977c478bd9Sstevel@tonic-gate 		    svp->sv_knconf->knc_protofmly) == 0 &&
3987c478bd9Sstevel@tonic-gate 		    bcmp(si->sv_addr.buf, svp->sv_addr.buf,
3997c478bd9Sstevel@tonic-gate 		    si->sv_addr.len) == 0) {
4007c478bd9Sstevel@tonic-gate 			/* it's a duplicate */
4017c478bd9Sstevel@tonic-gate 			return (1);
4027c478bd9Sstevel@tonic-gate 		}
4037c478bd9Sstevel@tonic-gate 	}
4047c478bd9Sstevel@tonic-gate 	/* it's not a duplicate */
4057c478bd9Sstevel@tonic-gate 	return (0);
4067c478bd9Sstevel@tonic-gate }
4077c478bd9Sstevel@tonic-gate 
40839d3e169Sevanl void
nfs4_free_args(struct nfs_args * nargs)40939d3e169Sevanl nfs4_free_args(struct nfs_args *nargs)
4107c478bd9Sstevel@tonic-gate {
41139d3e169Sevanl 	if (nargs->knconf) {
41239d3e169Sevanl 		if (nargs->knconf->knc_protofmly)
41339d3e169Sevanl 			kmem_free(nargs->knconf->knc_protofmly,
41439d3e169Sevanl 			    KNC_STRSIZE);
41539d3e169Sevanl 		if (nargs->knconf->knc_proto)
41639d3e169Sevanl 			kmem_free(nargs->knconf->knc_proto, KNC_STRSIZE);
41739d3e169Sevanl 		kmem_free(nargs->knconf, sizeof (*nargs->knconf));
41839d3e169Sevanl 		nargs->knconf = NULL;
41939d3e169Sevanl 	}
42039d3e169Sevanl 
42139d3e169Sevanl 	if (nargs->fh) {
42239d3e169Sevanl 		kmem_free(nargs->fh, strlen(nargs->fh) + 1);
42339d3e169Sevanl 		nargs->fh = NULL;
42439d3e169Sevanl 	}
42539d3e169Sevanl 
42639d3e169Sevanl 	if (nargs->hostname) {
42739d3e169Sevanl 		kmem_free(nargs->hostname, strlen(nargs->hostname) + 1);
42839d3e169Sevanl 		nargs->hostname = NULL;
42939d3e169Sevanl 	}
43039d3e169Sevanl 
43139d3e169Sevanl 	if (nargs->addr) {
43239d3e169Sevanl 		if (nargs->addr->buf) {
43339d3e169Sevanl 			ASSERT(nargs->addr->len);
43439d3e169Sevanl 			kmem_free(nargs->addr->buf, nargs->addr->len);
43539d3e169Sevanl 		}
43639d3e169Sevanl 		kmem_free(nargs->addr, sizeof (struct netbuf));
43739d3e169Sevanl 		nargs->addr = NULL;
43839d3e169Sevanl 	}
43939d3e169Sevanl 
44039d3e169Sevanl 	if (nargs->syncaddr) {
44139d3e169Sevanl 		ASSERT(nargs->syncaddr->len);
44239d3e169Sevanl 		if (nargs->syncaddr->buf) {
44339d3e169Sevanl 			ASSERT(nargs->syncaddr->len);
44439d3e169Sevanl 			kmem_free(nargs->syncaddr->buf, nargs->syncaddr->len);
44539d3e169Sevanl 		}
44639d3e169Sevanl 		kmem_free(nargs->syncaddr, sizeof (struct netbuf));
44739d3e169Sevanl 		nargs->syncaddr = NULL;
44839d3e169Sevanl 	}
44939d3e169Sevanl 
45039d3e169Sevanl 	if (nargs->netname) {
45139d3e169Sevanl 		kmem_free(nargs->netname, strlen(nargs->netname) + 1);
45239d3e169Sevanl 		nargs->netname = NULL;
45339d3e169Sevanl 	}
45439d3e169Sevanl 
45539d3e169Sevanl 	if (nargs->nfs_ext_u.nfs_extA.secdata) {
45639d3e169Sevanl 		sec_clnt_freeinfo(
45739d3e169Sevanl 		    nargs->nfs_ext_u.nfs_extA.secdata);
45839d3e169Sevanl 		nargs->nfs_ext_u.nfs_extA.secdata = NULL;
45939d3e169Sevanl 	}
46039d3e169Sevanl }
46139d3e169Sevanl 
46239d3e169Sevanl 
46339d3e169Sevanl int
nfs4_copyin(char * data,int datalen,struct nfs_args * nargs)46439d3e169Sevanl nfs4_copyin(char *data, int datalen, struct nfs_args *nargs)
46539d3e169Sevanl {
46639d3e169Sevanl 
4677c478bd9Sstevel@tonic-gate 	int error;
4687c478bd9Sstevel@tonic-gate 	size_t hlen;			/* length of hostname */
4697c478bd9Sstevel@tonic-gate 	size_t nlen;			/* length of netname */
4707c478bd9Sstevel@tonic-gate 	char netname[MAXNETNAMELEN+1];	/* server's netname */
4717c478bd9Sstevel@tonic-gate 	struct netbuf addr;		/* server's address */
4727c478bd9Sstevel@tonic-gate 	struct netbuf syncaddr;		/* AUTH_DES time sync addr */
47339d3e169Sevanl 	struct knetconfig *knconf;		/* transport structure */
47439d3e169Sevanl 	struct sec_data *secdata = NULL;	/* security data */
4757c478bd9Sstevel@tonic-gate 	STRUCT_DECL(nfs_args, args);		/* nfs mount arguments */
4767c478bd9Sstevel@tonic-gate 	STRUCT_DECL(knetconfig, knconf_tmp);
4777c478bd9Sstevel@tonic-gate 	STRUCT_DECL(netbuf, addr_tmp);
47839d3e169Sevanl 	int flags;
4797c478bd9Sstevel@tonic-gate 	char *p, *pf;
4807c478bd9Sstevel@tonic-gate 	struct pathname pn;
4817c478bd9Sstevel@tonic-gate 	char *userbufptr;
4827c478bd9Sstevel@tonic-gate 
48339d3e169Sevanl 
48439d3e169Sevanl 	bzero(nargs, sizeof (*nargs));
48539d3e169Sevanl 
4867c478bd9Sstevel@tonic-gate 	STRUCT_INIT(args, get_udatamodel());
4877c478bd9Sstevel@tonic-gate 	bzero(STRUCT_BUF(args), SIZEOF_STRUCT(nfs_args, DATAMODEL_NATIVE));
48839d3e169Sevanl 	if (copyin(data, STRUCT_BUF(args), MIN(datalen,
4897c478bd9Sstevel@tonic-gate 	    STRUCT_SIZE(args))))
4907c478bd9Sstevel@tonic-gate 		return (EFAULT);
4917c478bd9Sstevel@tonic-gate 
49239d3e169Sevanl 	nargs->wsize = STRUCT_FGET(args, wsize);
49339d3e169Sevanl 	nargs->rsize = STRUCT_FGET(args, rsize);
49439d3e169Sevanl 	nargs->timeo = STRUCT_FGET(args, timeo);
49539d3e169Sevanl 	nargs->retrans = STRUCT_FGET(args, retrans);
49639d3e169Sevanl 	nargs->acregmin = STRUCT_FGET(args, acregmin);
49739d3e169Sevanl 	nargs->acregmax = STRUCT_FGET(args, acregmax);
49839d3e169Sevanl 	nargs->acdirmin = STRUCT_FGET(args, acdirmin);
49939d3e169Sevanl 	nargs->acdirmax = STRUCT_FGET(args, acdirmax);
50039d3e169Sevanl 
5017c478bd9Sstevel@tonic-gate 	flags = STRUCT_FGET(args, flags);
50239d3e169Sevanl 	nargs->flags = flags;
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	addr.buf = NULL;
5057c478bd9Sstevel@tonic-gate 	syncaddr.buf = NULL;
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	/*
5097c478bd9Sstevel@tonic-gate 	 * Allocate space for a knetconfig structure and
5107c478bd9Sstevel@tonic-gate 	 * its strings and copy in from user-land.
5117c478bd9Sstevel@tonic-gate 	 */
5127c478bd9Sstevel@tonic-gate 	knconf = kmem_zalloc(sizeof (*knconf), KM_SLEEP);
5137c478bd9Sstevel@tonic-gate 	STRUCT_INIT(knconf_tmp, get_udatamodel());
5147c478bd9Sstevel@tonic-gate 	if (copyin(STRUCT_FGETP(args, knconf), STRUCT_BUF(knconf_tmp),
5157c478bd9Sstevel@tonic-gate 	    STRUCT_SIZE(knconf_tmp))) {
51639d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
5177c478bd9Sstevel@tonic-gate 		return (EFAULT);
5187c478bd9Sstevel@tonic-gate 	}
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	knconf->knc_semantics = STRUCT_FGET(knconf_tmp, knc_semantics);
5217c478bd9Sstevel@tonic-gate 	knconf->knc_protofmly = STRUCT_FGETP(knconf_tmp, knc_protofmly);
5227c478bd9Sstevel@tonic-gate 	knconf->knc_proto = STRUCT_FGETP(knconf_tmp, knc_proto);
5237c478bd9Sstevel@tonic-gate 	if (get_udatamodel() != DATAMODEL_LP64) {
5247c478bd9Sstevel@tonic-gate 		knconf->knc_rdev = expldev(STRUCT_FGET(knconf_tmp, knc_rdev));
5257c478bd9Sstevel@tonic-gate 	} else {
5267c478bd9Sstevel@tonic-gate 		knconf->knc_rdev = STRUCT_FGET(knconf_tmp, knc_rdev);
5277c478bd9Sstevel@tonic-gate 	}
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
5307c478bd9Sstevel@tonic-gate 	p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
5317c478bd9Sstevel@tonic-gate 	error = copyinstr(knconf->knc_protofmly, pf, KNC_STRSIZE, NULL);
5327c478bd9Sstevel@tonic-gate 	if (error) {
5337c478bd9Sstevel@tonic-gate 		kmem_free(pf, KNC_STRSIZE);
5347c478bd9Sstevel@tonic-gate 		kmem_free(p, KNC_STRSIZE);
53539d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
5367c478bd9Sstevel@tonic-gate 		return (error);
5377c478bd9Sstevel@tonic-gate 	}
53839d3e169Sevanl 
5397c478bd9Sstevel@tonic-gate 	error = copyinstr(knconf->knc_proto, p, KNC_STRSIZE, NULL);
5407c478bd9Sstevel@tonic-gate 	if (error) {
5417c478bd9Sstevel@tonic-gate 		kmem_free(pf, KNC_STRSIZE);
5427c478bd9Sstevel@tonic-gate 		kmem_free(p, KNC_STRSIZE);
54339d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
5447c478bd9Sstevel@tonic-gate 		return (error);
5457c478bd9Sstevel@tonic-gate 	}
54639d3e169Sevanl 
54739d3e169Sevanl 
5487c478bd9Sstevel@tonic-gate 	knconf->knc_protofmly = pf;
5497c478bd9Sstevel@tonic-gate 	knconf->knc_proto = p;
5507c478bd9Sstevel@tonic-gate 
55139d3e169Sevanl 	nargs->knconf = knconf;
55239d3e169Sevanl 
5537c478bd9Sstevel@tonic-gate 	/*
5547c478bd9Sstevel@tonic-gate 	 * Get server address
5557c478bd9Sstevel@tonic-gate 	 */
5567c478bd9Sstevel@tonic-gate 	STRUCT_INIT(addr_tmp, get_udatamodel());
5577c478bd9Sstevel@tonic-gate 	if (copyin(STRUCT_FGETP(args, addr), STRUCT_BUF(addr_tmp),
5587c478bd9Sstevel@tonic-gate 	    STRUCT_SIZE(addr_tmp))) {
5597c478bd9Sstevel@tonic-gate 		error = EFAULT;
5607c478bd9Sstevel@tonic-gate 		goto errout;
5617c478bd9Sstevel@tonic-gate 	}
5627c478bd9Sstevel@tonic-gate 
5637e450cbcSJames Wahlig 	nargs->addr = kmem_zalloc(sizeof (struct netbuf), KM_SLEEP);
56439d3e169Sevanl 	userbufptr = STRUCT_FGETP(addr_tmp, buf);
5657c478bd9Sstevel@tonic-gate 	addr.len = STRUCT_FGET(addr_tmp, len);
5667c478bd9Sstevel@tonic-gate 	addr.buf = kmem_alloc(addr.len, KM_SLEEP);
5677c478bd9Sstevel@tonic-gate 	addr.maxlen = addr.len;
5687c478bd9Sstevel@tonic-gate 	if (copyin(userbufptr, addr.buf, addr.len)) {
5697c478bd9Sstevel@tonic-gate 		kmem_free(addr.buf, addr.len);
5707c478bd9Sstevel@tonic-gate 		error = EFAULT;
5717c478bd9Sstevel@tonic-gate 		goto errout;
5727c478bd9Sstevel@tonic-gate 	}
57339d3e169Sevanl 	bcopy(&addr, nargs->addr, sizeof (struct netbuf));
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	/*
5767c478bd9Sstevel@tonic-gate 	 * Get the root fhandle
5777c478bd9Sstevel@tonic-gate 	 */
5787c478bd9Sstevel@tonic-gate 	error = pn_get(STRUCT_FGETP(args, fh), UIO_USERSPACE, &pn);
5797c478bd9Sstevel@tonic-gate 	if (error)
5807c478bd9Sstevel@tonic-gate 		goto errout;
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	/* Volatile fh: keep server paths, so use actual-size strings */
58339d3e169Sevanl 	nargs->fh = kmem_alloc(pn.pn_pathlen + 1, KM_SLEEP);
58439d3e169Sevanl 	bcopy(pn.pn_path, nargs->fh, pn.pn_pathlen);
58539d3e169Sevanl 	nargs->fh[pn.pn_pathlen] = '\0';
5867c478bd9Sstevel@tonic-gate 	pn_free(&pn);
5877c478bd9Sstevel@tonic-gate 
58839d3e169Sevanl 
5897c478bd9Sstevel@tonic-gate 	/*
5907c478bd9Sstevel@tonic-gate 	 * Get server's hostname
5917c478bd9Sstevel@tonic-gate 	 */
5927c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_HOSTNAME) {
5937c478bd9Sstevel@tonic-gate 		error = copyinstr(STRUCT_FGETP(args, hostname),
5947c478bd9Sstevel@tonic-gate 		    netname, sizeof (netname), &hlen);
5957c478bd9Sstevel@tonic-gate 		if (error)
5967c478bd9Sstevel@tonic-gate 			goto errout;
59739d3e169Sevanl 		nargs->hostname = kmem_zalloc(hlen, KM_SLEEP);
59839d3e169Sevanl 		(void) strcpy(nargs->hostname, netname);
59939d3e169Sevanl 
60039d3e169Sevanl 	} else {
60139d3e169Sevanl 		nargs->hostname = NULL;
60239d3e169Sevanl 	}
60339d3e169Sevanl 
60439d3e169Sevanl 
60539d3e169Sevanl 	/*
60639d3e169Sevanl 	 * If there are syncaddr and netname data, load them in. This is
60739d3e169Sevanl 	 * to support data needed for NFSV4 when AUTH_DH is the negotiated
60839d3e169Sevanl 	 * flavor via SECINFO. (instead of using MOUNT protocol in V3).
60939d3e169Sevanl 	 */
61039d3e169Sevanl 	netname[0] = '\0';
61139d3e169Sevanl 	if (flags & NFSMNT_SECURE) {
61239d3e169Sevanl 
61339d3e169Sevanl 		/* get syncaddr */
61439d3e169Sevanl 		STRUCT_INIT(addr_tmp, get_udatamodel());
61539d3e169Sevanl 		if (copyin(STRUCT_FGETP(args, syncaddr), STRUCT_BUF(addr_tmp),
61639d3e169Sevanl 		    STRUCT_SIZE(addr_tmp))) {
61739d3e169Sevanl 			error = EINVAL;
61839d3e169Sevanl 			goto errout;
61939d3e169Sevanl 		}
62039d3e169Sevanl 		userbufptr = STRUCT_FGETP(addr_tmp, buf);
62139d3e169Sevanl 		syncaddr.len = STRUCT_FGET(addr_tmp, len);
62239d3e169Sevanl 		syncaddr.buf = kmem_alloc(syncaddr.len, KM_SLEEP);
62339d3e169Sevanl 		syncaddr.maxlen = syncaddr.len;
62439d3e169Sevanl 		if (copyin(userbufptr, syncaddr.buf, syncaddr.len)) {
62539d3e169Sevanl 			kmem_free(syncaddr.buf, syncaddr.len);
62639d3e169Sevanl 			error = EFAULT;
62739d3e169Sevanl 			goto errout;
62839d3e169Sevanl 		}
62939d3e169Sevanl 
63039d3e169Sevanl 		nargs->syncaddr = kmem_alloc(sizeof (struct netbuf), KM_SLEEP);
63139d3e169Sevanl 		bcopy(&syncaddr, nargs->syncaddr, sizeof (struct netbuf));
63222d5e933Skr143551 
63339d3e169Sevanl 		/* get server's netname */
63439d3e169Sevanl 		if (copyinstr(STRUCT_FGETP(args, netname), netname,
63539d3e169Sevanl 		    sizeof (netname), &nlen)) {
63639d3e169Sevanl 			error = EFAULT;
63739d3e169Sevanl 			goto errout;
63839d3e169Sevanl 		}
63939d3e169Sevanl 
64039d3e169Sevanl 		netname[nlen] = '\0';
64139d3e169Sevanl 		nargs->netname = kmem_zalloc(nlen, KM_SLEEP);
64239d3e169Sevanl 		(void) strcpy(nargs->netname, netname);
64322d5e933Skr143551 	}
64439d3e169Sevanl 
64539d3e169Sevanl 	/*
64639d3e169Sevanl 	 * Get the extention data which has the security data structure.
64739d3e169Sevanl 	 * This includes data for AUTH_SYS as well.
64839d3e169Sevanl 	 */
64939d3e169Sevanl 	if (flags & NFSMNT_NEWARGS) {
65039d3e169Sevanl 		nargs->nfs_args_ext = STRUCT_FGET(args, nfs_args_ext);
65139d3e169Sevanl 		if (nargs->nfs_args_ext == NFS_ARGS_EXTA ||
65239d3e169Sevanl 		    nargs->nfs_args_ext == NFS_ARGS_EXTB) {
65339d3e169Sevanl 			/*
65439d3e169Sevanl 			 * Indicating the application is using the new
65539d3e169Sevanl 			 * sec_data structure to pass in the security
65639d3e169Sevanl 			 * data.
65739d3e169Sevanl 			 */
65839d3e169Sevanl 			if (STRUCT_FGETP(args,
65939d3e169Sevanl 			    nfs_ext_u.nfs_extA.secdata) != NULL) {
66039d3e169Sevanl 				error = sec_clnt_loadinfo(
66139d3e169Sevanl 				    (struct sec_data *)STRUCT_FGETP(args,
66239d3e169Sevanl 				    nfs_ext_u.nfs_extA.secdata),
66339d3e169Sevanl 				    &secdata, get_udatamodel());
66439d3e169Sevanl 			}
66539d3e169Sevanl 			nargs->nfs_ext_u.nfs_extA.secdata = secdata;
66639d3e169Sevanl 		}
66739d3e169Sevanl 	}
66839d3e169Sevanl 
66939d3e169Sevanl 	if (error)
67039d3e169Sevanl 		goto errout;
67139d3e169Sevanl 
67239d3e169Sevanl 	/*
67339d3e169Sevanl 	 * Failover support:
67439d3e169Sevanl 	 *
67539d3e169Sevanl 	 * We may have a linked list of nfs_args structures,
67639d3e169Sevanl 	 * which means the user is looking for failover.  If
67739d3e169Sevanl 	 * the mount is either not "read-only" or "soft",
67839d3e169Sevanl 	 * we want to bail out with EINVAL.
67939d3e169Sevanl 	 */
68039d3e169Sevanl 	if (nargs->nfs_args_ext == NFS_ARGS_EXTB)
68139d3e169Sevanl 		nargs->nfs_ext_u.nfs_extB.next =
68239d3e169Sevanl 		    STRUCT_FGETP(args, nfs_ext_u.nfs_extB.next);
68339d3e169Sevanl 
68439d3e169Sevanl errout:
68539d3e169Sevanl 	if (error)
68639d3e169Sevanl 		nfs4_free_args(nargs);
68739d3e169Sevanl 
68839d3e169Sevanl 	return (error);
68939d3e169Sevanl }
69039d3e169Sevanl 
69139d3e169Sevanl 
69239d3e169Sevanl /*
69339d3e169Sevanl  * nfs mount vfsop
69439d3e169Sevanl  * Set up mount info record and attach it to vfs struct.
69539d3e169Sevanl  */
696b9238976Sth199096 int
nfs4_mount(vfs_t * vfsp,vnode_t * mvp,struct mounta * uap,cred_t * cr)69739d3e169Sevanl nfs4_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
69839d3e169Sevanl {
69939d3e169Sevanl 	char *data = uap->dataptr;
70039d3e169Sevanl 	int error;
70139d3e169Sevanl 	vnode_t *rtvp;			/* the server's root */
70239d3e169Sevanl 	mntinfo4_t *mi;			/* mount info, pointed at by vfs */
70339d3e169Sevanl 	struct knetconfig *rdma_knconf;	/* rdma transport structure */
70439d3e169Sevanl 	rnode4_t *rp;
70539d3e169Sevanl 	struct servinfo4 *svp;		/* nfs server info */
70639d3e169Sevanl 	struct servinfo4 *svp_tail = NULL; /* previous nfs server info */
70739d3e169Sevanl 	struct servinfo4 *svp_head;	/* first nfs server info */
70839d3e169Sevanl 	struct servinfo4 *svp_2ndlast;	/* 2nd last in server info list */
70939d3e169Sevanl 	struct sec_data *secdata;	/* security data */
71039d3e169Sevanl 	struct nfs_args *args = NULL;
71139d3e169Sevanl 	int flags, addr_type, removed;
71239d3e169Sevanl 	zone_t *zone = nfs_zone();
71339d3e169Sevanl 	nfs4_error_t n4e;
71439d3e169Sevanl 	zone_t *mntzone = NULL;
71539d3e169Sevanl 
71639d3e169Sevanl 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
71739d3e169Sevanl 		return (EPERM);
71839d3e169Sevanl 	if (mvp->v_type != VDIR)
71939d3e169Sevanl 		return (ENOTDIR);
720b9238976Sth199096 
72139d3e169Sevanl 	/*
72239d3e169Sevanl 	 * get arguments
72339d3e169Sevanl 	 *
72439d3e169Sevanl 	 * nfs_args is now versioned and is extensible, so
72539d3e169Sevanl 	 * uap->datalen might be different from sizeof (args)
72639d3e169Sevanl 	 * in a compatible situation.
72739d3e169Sevanl 	 */
72839d3e169Sevanl more:
72939d3e169Sevanl 	if (!(uap->flags & MS_SYSSPACE)) {
73039d3e169Sevanl 		if (args == NULL)
73139d3e169Sevanl 			args = kmem_zalloc(sizeof (struct nfs_args), KM_SLEEP);
73239d3e169Sevanl 		else
73339d3e169Sevanl 			nfs4_free_args(args);
73439d3e169Sevanl 		error = nfs4_copyin(data, uap->datalen, args);
73539d3e169Sevanl 		if (error) {
73639d3e169Sevanl 			if (args) {
73739d3e169Sevanl 				kmem_free(args, sizeof (*args));
73839d3e169Sevanl 			}
73939d3e169Sevanl 			return (error);
74039d3e169Sevanl 		}
74139d3e169Sevanl 	} else {
74239d3e169Sevanl 		args = (struct nfs_args *)data;
74339d3e169Sevanl 	}
74439d3e169Sevanl 
74539d3e169Sevanl 	flags = args->flags;
74639d3e169Sevanl 
74739d3e169Sevanl 	/*
74839d3e169Sevanl 	 * If the request changes the locking type, disallow the remount,
74939d3e169Sevanl 	 * because it's questionable whether we can transfer the
75039d3e169Sevanl 	 * locking state correctly.
75139d3e169Sevanl 	 */
75239d3e169Sevanl 	if (uap->flags & MS_REMOUNT) {
75339d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
75439d3e169Sevanl 			nfs4_free_args(args);
75539d3e169Sevanl 			kmem_free(args, sizeof (*args));
75639d3e169Sevanl 		}
75739d3e169Sevanl 		if ((mi = VFTOMI4(vfsp)) != NULL) {
75839d3e169Sevanl 			uint_t new_mi_llock;
75939d3e169Sevanl 			uint_t old_mi_llock;
76039d3e169Sevanl 			new_mi_llock = (flags & NFSMNT_LLOCK) ? 1 : 0;
76139d3e169Sevanl 			old_mi_llock = (mi->mi_flags & MI4_LLOCK) ? 1 : 0;
76239d3e169Sevanl 			if (old_mi_llock != new_mi_llock)
76339d3e169Sevanl 				return (EBUSY);
76439d3e169Sevanl 		}
76539d3e169Sevanl 		return (0);
76639d3e169Sevanl 	}
76739d3e169Sevanl 
768b9238976Sth199096 	/*
769b9238976Sth199096 	 * For ephemeral mount trigger stub vnodes, we have two problems
770b9238976Sth199096 	 * to solve: racing threads will likely fail the v_count check, and
771b9238976Sth199096 	 * we want only one to proceed with the mount.
772b9238976Sth199096 	 *
773b9238976Sth199096 	 * For stubs, if the mount has already occurred (via a racing thread),
774b9238976Sth199096 	 * just return success. If not, skip the v_count check and proceed.
775b9238976Sth199096 	 * Note that we are already serialised at this point.
776b9238976Sth199096 	 */
77739d3e169Sevanl 	mutex_enter(&mvp->v_lock);
778b9238976Sth199096 	if (vn_matchops(mvp, nfs4_trigger_vnodeops)) {
779b9238976Sth199096 		/* mntpt is a v4 stub vnode */
780b9238976Sth199096 		ASSERT(RP_ISSTUB(VTOR4(mvp)));
781b9238976Sth199096 		ASSERT(!(uap->flags & MS_OVERLAY));
782b9238976Sth199096 		ASSERT(!(mvp->v_flag & VROOT));
783b9238976Sth199096 		if (vn_mountedvfs(mvp) != NULL) {
784b9238976Sth199096 			/* ephemeral mount has already occurred */
785b9238976Sth199096 			ASSERT(uap->flags & MS_SYSSPACE);
786b9238976Sth199096 			mutex_exit(&mvp->v_lock);
787b9238976Sth199096 			return (0);
788b9238976Sth199096 		}
789b9238976Sth199096 	} else {
790b9238976Sth199096 		/* mntpt is a non-v4 or v4 non-stub vnode */
79139d3e169Sevanl 		if (!(uap->flags & MS_OVERLAY) &&
79239d3e169Sevanl 		    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
79339d3e169Sevanl 			mutex_exit(&mvp->v_lock);
79439d3e169Sevanl 			if (!(uap->flags & MS_SYSSPACE)) {
79539d3e169Sevanl 				nfs4_free_args(args);
79639d3e169Sevanl 				kmem_free(args, sizeof (*args));
79739d3e169Sevanl 			}
79839d3e169Sevanl 			return (EBUSY);
79939d3e169Sevanl 		}
800b9238976Sth199096 	}
80139d3e169Sevanl 	mutex_exit(&mvp->v_lock);
80239d3e169Sevanl 
80339d3e169Sevanl 	/* make sure things are zeroed for errout: */
80439d3e169Sevanl 	rtvp = NULL;
80539d3e169Sevanl 	mi = NULL;
80639d3e169Sevanl 	secdata = NULL;
80739d3e169Sevanl 
80839d3e169Sevanl 	/*
80939d3e169Sevanl 	 * A valid knetconfig structure is required.
81039d3e169Sevanl 	 */
81139d3e169Sevanl 	if (!(flags & NFSMNT_KNCONF) ||
81239d3e169Sevanl 	    args->knconf == NULL || args->knconf->knc_protofmly == NULL ||
81339d3e169Sevanl 	    args->knconf->knc_proto == NULL ||
81439d3e169Sevanl 	    (strcmp(args->knconf->knc_proto, NC_UDP) == 0)) {
81539d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
81639d3e169Sevanl 			nfs4_free_args(args);
81739d3e169Sevanl 			kmem_free(args, sizeof (*args));
81839d3e169Sevanl 		}
81939d3e169Sevanl 		return (EINVAL);
82039d3e169Sevanl 	}
82139d3e169Sevanl 
82239d3e169Sevanl 	if ((strlen(args->knconf->knc_protofmly) >= KNC_STRSIZE) ||
82339d3e169Sevanl 	    (strlen(args->knconf->knc_proto) >= KNC_STRSIZE)) {
82439d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
82539d3e169Sevanl 			nfs4_free_args(args);
82639d3e169Sevanl 			kmem_free(args, sizeof (*args));
82739d3e169Sevanl 		}
82839d3e169Sevanl 		return (EINVAL);
82939d3e169Sevanl 	}
83039d3e169Sevanl 
83139d3e169Sevanl 	/*
83239d3e169Sevanl 	 * Allocate a servinfo4 struct.
83339d3e169Sevanl 	 */
83439d3e169Sevanl 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
83539d3e169Sevanl 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
83639d3e169Sevanl 	if (svp_tail) {
83739d3e169Sevanl 		svp_2ndlast = svp_tail;
83839d3e169Sevanl 		svp_tail->sv_next = svp;
83939d3e169Sevanl 	} else {
84039d3e169Sevanl 		svp_head = svp;
84139d3e169Sevanl 		svp_2ndlast = svp;
84239d3e169Sevanl 	}
84339d3e169Sevanl 
84439d3e169Sevanl 	svp_tail = svp;
84539d3e169Sevanl 	svp->sv_knconf = args->knconf;
84639d3e169Sevanl 	args->knconf = NULL;
84739d3e169Sevanl 
84839d3e169Sevanl 	/*
84939d3e169Sevanl 	 * Get server address
85039d3e169Sevanl 	 */
85139d3e169Sevanl 	if (args->addr == NULL || args->addr->buf == NULL) {
85239d3e169Sevanl 		error = EINVAL;
85339d3e169Sevanl 		goto errout;
85439d3e169Sevanl 	}
85539d3e169Sevanl 
85639d3e169Sevanl 	svp->sv_addr.maxlen = args->addr->maxlen;
85739d3e169Sevanl 	svp->sv_addr.len = args->addr->len;
85839d3e169Sevanl 	svp->sv_addr.buf = args->addr->buf;
85939d3e169Sevanl 	args->addr->buf = NULL;
86039d3e169Sevanl 
86139d3e169Sevanl 	/*
86239d3e169Sevanl 	 * Get the root fhandle
86339d3e169Sevanl 	 */
86439d3e169Sevanl 	if (args->fh == NULL || (strlen(args->fh) >= MAXPATHLEN)) {
86539d3e169Sevanl 		error = EINVAL;
86639d3e169Sevanl 		goto errout;
86739d3e169Sevanl 	}
86839d3e169Sevanl 
86939d3e169Sevanl 	svp->sv_path = args->fh;
87039d3e169Sevanl 	svp->sv_pathlen = strlen(args->fh) + 1;
87139d3e169Sevanl 	args->fh = NULL;
87239d3e169Sevanl 
87339d3e169Sevanl 	/*
87439d3e169Sevanl 	 * Get server's hostname
87539d3e169Sevanl 	 */
87639d3e169Sevanl 	if (flags & NFSMNT_HOSTNAME) {
87739d3e169Sevanl 		if (args->hostname == NULL || (strlen(args->hostname) >
87839d3e169Sevanl 		    MAXNETNAMELEN)) {
87939d3e169Sevanl 			error = EINVAL;
88039d3e169Sevanl 			goto errout;
88139d3e169Sevanl 		}
88239d3e169Sevanl 		svp->sv_hostnamelen = strlen(args->hostname) + 1;
88339d3e169Sevanl 		svp->sv_hostname = args->hostname;
88439d3e169Sevanl 		args->hostname = NULL;
8857c478bd9Sstevel@tonic-gate 	} else {
8867c478bd9Sstevel@tonic-gate 		char *p = "unknown-host";
88739d3e169Sevanl 		svp->sv_hostnamelen = strlen(p) + 1;
88839d3e169Sevanl 		svp->sv_hostname = kmem_zalloc(svp->sv_hostnamelen, KM_SLEEP);
88939d3e169Sevanl 		(void) strcpy(svp->sv_hostname, p);
8907c478bd9Sstevel@tonic-gate 	}
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 	/*
8937c478bd9Sstevel@tonic-gate 	 * RDMA MOUNT SUPPORT FOR NFS v4.
8947c478bd9Sstevel@tonic-gate 	 * Establish, is it possible to use RDMA, if so overload the
8957c478bd9Sstevel@tonic-gate 	 * knconf with rdma specific knconf and free the orignal knconf.
8967c478bd9Sstevel@tonic-gate 	 */
8977c478bd9Sstevel@tonic-gate 	if ((flags & NFSMNT_TRYRDMA) || (flags & NFSMNT_DORDMA)) {
8987c478bd9Sstevel@tonic-gate 		/*
8997c478bd9Sstevel@tonic-gate 		 * Determine the addr type for RDMA, IPv4 or v6.
9007c478bd9Sstevel@tonic-gate 		 */
9017c478bd9Sstevel@tonic-gate 		if (strcmp(svp->sv_knconf->knc_protofmly, NC_INET) == 0)
9027c478bd9Sstevel@tonic-gate 			addr_type = AF_INET;
9037c478bd9Sstevel@tonic-gate 		else if (strcmp(svp->sv_knconf->knc_protofmly, NC_INET6) == 0)
9047c478bd9Sstevel@tonic-gate 			addr_type = AF_INET6;
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		if (rdma_reachable(addr_type, &svp->sv_addr,
9077c478bd9Sstevel@tonic-gate 		    &rdma_knconf) == 0) {
9087c478bd9Sstevel@tonic-gate 			/*
9097c478bd9Sstevel@tonic-gate 			 * If successful, hijack the orignal knconf and
9107c478bd9Sstevel@tonic-gate 			 * replace with the new one, depending on the flags.
9117c478bd9Sstevel@tonic-gate 			 */
9127c478bd9Sstevel@tonic-gate 			svp->sv_origknconf = svp->sv_knconf;
9137c478bd9Sstevel@tonic-gate 			svp->sv_knconf = rdma_knconf;
9147c478bd9Sstevel@tonic-gate 		} else {
9157c478bd9Sstevel@tonic-gate 			if (flags & NFSMNT_TRYRDMA) {
9167c478bd9Sstevel@tonic-gate #ifdef	DEBUG
9177c478bd9Sstevel@tonic-gate 				if (rdma_debug)
9187c478bd9Sstevel@tonic-gate 					zcmn_err(getzoneid(), CE_WARN,
9197c478bd9Sstevel@tonic-gate 					    "no RDMA onboard, revert\n");
9207c478bd9Sstevel@tonic-gate #endif
9217c478bd9Sstevel@tonic-gate 			}
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 			if (flags & NFSMNT_DORDMA) {
9247c478bd9Sstevel@tonic-gate 				/*
9257c478bd9Sstevel@tonic-gate 				 * If proto=rdma is specified and no RDMA
9267c478bd9Sstevel@tonic-gate 				 * path to this server is avialable then
9277c478bd9Sstevel@tonic-gate 				 * ditch this server.
9287c478bd9Sstevel@tonic-gate 				 * This is not included in the mountable
9297c478bd9Sstevel@tonic-gate 				 * server list or the replica list.
9307c478bd9Sstevel@tonic-gate 				 * Check if more servers are specified;
9317c478bd9Sstevel@tonic-gate 				 * Failover case, otherwise bail out of mount.
9327c478bd9Sstevel@tonic-gate 				 */
933b9238976Sth199096 				if (args->nfs_args_ext == NFS_ARGS_EXTB &&
934b9238976Sth199096 				    args->nfs_ext_u.nfs_extB.next != NULL) {
93539d3e169Sevanl 					data = (char *)
93639d3e169Sevanl 					    args->nfs_ext_u.nfs_extB.next;
9377c478bd9Sstevel@tonic-gate 					if (uap->flags & MS_RDONLY &&
9387c478bd9Sstevel@tonic-gate 					    !(flags & NFSMNT_SOFT)) {
9397c478bd9Sstevel@tonic-gate 						if (svp_head->sv_next == NULL) {
9407c478bd9Sstevel@tonic-gate 							svp_tail = NULL;
9417c478bd9Sstevel@tonic-gate 							svp_2ndlast = NULL;
9427c478bd9Sstevel@tonic-gate 							sv4_free(svp_head);
9437c478bd9Sstevel@tonic-gate 							goto more;
9447c478bd9Sstevel@tonic-gate 						} else {
9457c478bd9Sstevel@tonic-gate 							svp_tail = svp_2ndlast;
9467c478bd9Sstevel@tonic-gate 							svp_2ndlast->sv_next =
9477c478bd9Sstevel@tonic-gate 							    NULL;
9487c478bd9Sstevel@tonic-gate 							sv4_free(svp);
9497c478bd9Sstevel@tonic-gate 							goto more;
9507c478bd9Sstevel@tonic-gate 						}
9517c478bd9Sstevel@tonic-gate 					}
9527c478bd9Sstevel@tonic-gate 				} else {
9537c478bd9Sstevel@tonic-gate 					/*
9547c478bd9Sstevel@tonic-gate 					 * This is the last server specified
9557c478bd9Sstevel@tonic-gate 					 * in the nfs_args list passed down
9567c478bd9Sstevel@tonic-gate 					 * and its not rdma capable.
9577c478bd9Sstevel@tonic-gate 					 */
9587c478bd9Sstevel@tonic-gate 					if (svp_head->sv_next == NULL) {
9597c478bd9Sstevel@tonic-gate 						/*
9607c478bd9Sstevel@tonic-gate 						 * Is this the only one
9617c478bd9Sstevel@tonic-gate 						 */
9627c478bd9Sstevel@tonic-gate 						error = EINVAL;
9637c478bd9Sstevel@tonic-gate #ifdef	DEBUG
9647c478bd9Sstevel@tonic-gate 						if (rdma_debug)
9657c478bd9Sstevel@tonic-gate 							zcmn_err(getzoneid(),
9667c478bd9Sstevel@tonic-gate 							    CE_WARN,
9677c478bd9Sstevel@tonic-gate 							    "No RDMA srv");
9687c478bd9Sstevel@tonic-gate #endif
9697c478bd9Sstevel@tonic-gate 						goto errout;
9707c478bd9Sstevel@tonic-gate 					} else {
9717c478bd9Sstevel@tonic-gate 						/*
9727c478bd9Sstevel@tonic-gate 						 * There is list, since some
9737c478bd9Sstevel@tonic-gate 						 * servers specified before
9747c478bd9Sstevel@tonic-gate 						 * this passed all requirements
9757c478bd9Sstevel@tonic-gate 						 */
9767c478bd9Sstevel@tonic-gate 						svp_tail = svp_2ndlast;
9777c478bd9Sstevel@tonic-gate 						svp_2ndlast->sv_next = NULL;
9787c478bd9Sstevel@tonic-gate 						sv4_free(svp);
9797c478bd9Sstevel@tonic-gate 						goto proceed;
9807c478bd9Sstevel@tonic-gate 					}
9817c478bd9Sstevel@tonic-gate 				}
9827c478bd9Sstevel@tonic-gate 			}
9837c478bd9Sstevel@tonic-gate 		}
9847c478bd9Sstevel@tonic-gate 	}
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	/*
9877c478bd9Sstevel@tonic-gate 	 * If there are syncaddr and netname data, load them in. This is
9887c478bd9Sstevel@tonic-gate 	 * to support data needed for NFSV4 when AUTH_DH is the negotiated
9897c478bd9Sstevel@tonic-gate 	 * flavor via SECINFO. (instead of using MOUNT protocol in V3).
9907c478bd9Sstevel@tonic-gate 	 */
99139d3e169Sevanl 	if (args->flags & NFSMNT_SECURE) {
99239d3e169Sevanl 		svp->sv_dhsec = create_authdh_data(args->netname,
99339d3e169Sevanl 		    strlen(args->netname),
99439d3e169Sevanl 		    args->syncaddr, svp->sv_knconf);
9957c478bd9Sstevel@tonic-gate 	}
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	/*
9987c478bd9Sstevel@tonic-gate 	 * Get the extention data which has the security data structure.
9997c478bd9Sstevel@tonic-gate 	 * This includes data for AUTH_SYS as well.
10007c478bd9Sstevel@tonic-gate 	 */
10017c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_NEWARGS) {
100239d3e169Sevanl 		switch (args->nfs_args_ext) {
10037c478bd9Sstevel@tonic-gate 		case NFS_ARGS_EXTA:
10047c478bd9Sstevel@tonic-gate 		case NFS_ARGS_EXTB:
10057c478bd9Sstevel@tonic-gate 			/*
10067c478bd9Sstevel@tonic-gate 			 * Indicating the application is using the new
10077c478bd9Sstevel@tonic-gate 			 * sec_data structure to pass in the security
10087c478bd9Sstevel@tonic-gate 			 * data.
10097c478bd9Sstevel@tonic-gate 			 */
101039d3e169Sevanl 			secdata = args->nfs_ext_u.nfs_extA.secdata;
101139d3e169Sevanl 			if (secdata == NULL) {
10127c478bd9Sstevel@tonic-gate 				error = EINVAL;
101339d3e169Sevanl 			} else if (uap->flags & MS_SYSSPACE) {
101439d3e169Sevanl 				/*
101539d3e169Sevanl 				 * Need to validate the flavor here if
101639d3e169Sevanl 				 * sysspace, userspace was already
101739d3e169Sevanl 				 * validate from the nfs_copyin function.
101839d3e169Sevanl 				 */
101939d3e169Sevanl 				switch (secdata->rpcflavor) {
102039d3e169Sevanl 				case AUTH_NONE:
102139d3e169Sevanl 				case AUTH_UNIX:
102239d3e169Sevanl 				case AUTH_LOOPBACK:
102339d3e169Sevanl 				case AUTH_DES:
102439d3e169Sevanl 				case RPCSEC_GSS:
102539d3e169Sevanl 					break;
102639d3e169Sevanl 				default:
102739d3e169Sevanl 					error = EINVAL;
102839d3e169Sevanl 					goto errout;
10297c478bd9Sstevel@tonic-gate 				}
103039d3e169Sevanl 			}
103139d3e169Sevanl 			args->nfs_ext_u.nfs_extA.secdata = NULL;
10327c478bd9Sstevel@tonic-gate 			break;
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 		default:
10357c478bd9Sstevel@tonic-gate 			error = EINVAL;
10367c478bd9Sstevel@tonic-gate 			break;
10377c478bd9Sstevel@tonic-gate 		}
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 	} else if (flags & NFSMNT_SECURE) {
10407c478bd9Sstevel@tonic-gate 		/*
10417c478bd9Sstevel@tonic-gate 		 * NFSMNT_SECURE is deprecated but we keep it
1042b9238976Sth199096 		 * to support the rogue user-generated application
10437c478bd9Sstevel@tonic-gate 		 * that may use this undocumented interface to do
1044b9238976Sth199096 		 * AUTH_DH security, e.g. our own rexd.
1045b9238976Sth199096 		 *
1046b9238976Sth199096 		 * Also note that NFSMNT_SECURE is used for passing
1047b9238976Sth199096 		 * AUTH_DH info to be used in negotiation.
10487c478bd9Sstevel@tonic-gate 		 */
104939d3e169Sevanl 		secdata = create_authdh_data(args->netname,
105039d3e169Sevanl 		    strlen(args->netname), args->syncaddr, svp->sv_knconf);
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	} else {
10537c478bd9Sstevel@tonic-gate 		secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
10547c478bd9Sstevel@tonic-gate 		secdata->secmod = secdata->rpcflavor = AUTH_SYS;
10557c478bd9Sstevel@tonic-gate 		secdata->data = NULL;
10567c478bd9Sstevel@tonic-gate 	}
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	svp->sv_secdata = secdata;
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	/*
10617c478bd9Sstevel@tonic-gate 	 * User does not explictly specify a flavor, and a user
10627c478bd9Sstevel@tonic-gate 	 * defined default flavor is passed down.
10637c478bd9Sstevel@tonic-gate 	 */
10647c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_SECDEFAULT) {
10657c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
10667c478bd9Sstevel@tonic-gate 		svp->sv_flags |= SV4_TRYSECDEFAULT;
10677c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
10687c478bd9Sstevel@tonic-gate 	}
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 	/*
10717c478bd9Sstevel@tonic-gate 	 * Failover support:
10727c478bd9Sstevel@tonic-gate 	 *
10737c478bd9Sstevel@tonic-gate 	 * We may have a linked list of nfs_args structures,
10747c478bd9Sstevel@tonic-gate 	 * which means the user is looking for failover.  If
10757c478bd9Sstevel@tonic-gate 	 * the mount is either not "read-only" or "soft",
10767c478bd9Sstevel@tonic-gate 	 * we want to bail out with EINVAL.
10777c478bd9Sstevel@tonic-gate 	 */
107839d3e169Sevanl 	if (args->nfs_args_ext == NFS_ARGS_EXTB &&
107939d3e169Sevanl 	    args->nfs_ext_u.nfs_extB.next != NULL) {
10807c478bd9Sstevel@tonic-gate 		if (uap->flags & MS_RDONLY && !(flags & NFSMNT_SOFT)) {
108139d3e169Sevanl 			data = (char *)args->nfs_ext_u.nfs_extB.next;
10827c478bd9Sstevel@tonic-gate 			goto more;
10837c478bd9Sstevel@tonic-gate 		}
10847c478bd9Sstevel@tonic-gate 		error = EINVAL;
10857c478bd9Sstevel@tonic-gate 		goto errout;
10867c478bd9Sstevel@tonic-gate 	}
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate 	/*
10897c478bd9Sstevel@tonic-gate 	 * Determine the zone we're being mounted into.
10907c478bd9Sstevel@tonic-gate 	 */
109145916cd2Sjpk 	zone_hold(mntzone = zone);		/* start with this assumption */
10927c478bd9Sstevel@tonic-gate 	if (getzoneid() == GLOBAL_ZONEID) {
109345916cd2Sjpk 		zone_rele(mntzone);
10947c478bd9Sstevel@tonic-gate 		mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
10957c478bd9Sstevel@tonic-gate 		ASSERT(mntzone != NULL);
10967c478bd9Sstevel@tonic-gate 		if (mntzone != zone) {
10977c478bd9Sstevel@tonic-gate 			error = EBUSY;
10987c478bd9Sstevel@tonic-gate 			goto errout;
10997c478bd9Sstevel@tonic-gate 		}
11007c478bd9Sstevel@tonic-gate 	}
11017c478bd9Sstevel@tonic-gate 
110245916cd2Sjpk 	if (is_system_labeled()) {
110345916cd2Sjpk 		error = nfs_mount_label_policy(vfsp, &svp->sv_addr,
110445916cd2Sjpk 		    svp->sv_knconf, cr);
110545916cd2Sjpk 
110645916cd2Sjpk 		if (error > 0)
110745916cd2Sjpk 			goto errout;
110845916cd2Sjpk 
110945916cd2Sjpk 		if (error == -1) {
111045916cd2Sjpk 			/* change mount to read-only to prevent write-down */
111145916cd2Sjpk 			vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
111245916cd2Sjpk 		}
111345916cd2Sjpk 	}
111445916cd2Sjpk 
11157c478bd9Sstevel@tonic-gate 	/*
11167c478bd9Sstevel@tonic-gate 	 * Stop the mount from going any further if the zone is going away.
11177c478bd9Sstevel@tonic-gate 	 */
111845916cd2Sjpk 	if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) {
11197c478bd9Sstevel@tonic-gate 		error = EBUSY;
11207c478bd9Sstevel@tonic-gate 		goto errout;
11217c478bd9Sstevel@tonic-gate 	}
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate 	/*
11247c478bd9Sstevel@tonic-gate 	 * Get root vnode.
11257c478bd9Sstevel@tonic-gate 	 */
11267c478bd9Sstevel@tonic-gate proceed:
112745916cd2Sjpk 	error = nfs4rootvp(&rtvp, vfsp, svp_head, flags, cr, mntzone);
112850a83466Sjwahlig 	if (error) {
112950a83466Sjwahlig 		/* if nfs4rootvp failed, it will free svp_head */
113050a83466Sjwahlig 		svp_head = NULL;
11317c478bd9Sstevel@tonic-gate 		goto errout;
113250a83466Sjwahlig 	}
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rtvp);
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	/*
11377c478bd9Sstevel@tonic-gate 	 * Send client id to the server, if necessary
11387c478bd9Sstevel@tonic-gate 	 */
11397c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&n4e);
11407c478bd9Sstevel@tonic-gate 	nfs4setclientid(mi, cr, FALSE, &n4e);
1141b9238976Sth199096 
11427c478bd9Sstevel@tonic-gate 	error = n4e.error;
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	if (error)
11457c478bd9Sstevel@tonic-gate 		goto errout;
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 	/*
11487c478bd9Sstevel@tonic-gate 	 * Set option fields in the mount info record
11497c478bd9Sstevel@tonic-gate 	 */
11507c478bd9Sstevel@tonic-gate 
11517c478bd9Sstevel@tonic-gate 	if (svp_head->sv_next) {
11527c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
11537c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_LLOCK;
11547c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
11557c478bd9Sstevel@tonic-gate 	}
115639d3e169Sevanl 	error = nfs4_setopts(rtvp, DATAMODEL_NATIVE, args);
1157b9238976Sth199096 	if (error)
1158b9238976Sth199096 		goto errout;
1159b9238976Sth199096 
1160b9238976Sth199096 	/*
1161b9238976Sth199096 	 * Time to tie in the mirror mount info at last!
1162b9238976Sth199096 	 */
1163b9238976Sth199096 	if (flags & NFSMNT_EPHEMERAL)
1164d3a14591SThomas Haynes 		error = nfs4_record_ephemeral_mount(mi, mvp);
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate errout:
11677c478bd9Sstevel@tonic-gate 	if (error) {
11687c478bd9Sstevel@tonic-gate 		if (rtvp != NULL) {
11697c478bd9Sstevel@tonic-gate 			rp = VTOR4(rtvp);
11707c478bd9Sstevel@tonic-gate 			if (rp->r_flags & R4HASHED)
11717c478bd9Sstevel@tonic-gate 				rp4_rmhash(rp);
11727c478bd9Sstevel@tonic-gate 		}
11737c478bd9Sstevel@tonic-gate 		if (mi != NULL) {
11747c478bd9Sstevel@tonic-gate 			nfs4_async_stop(vfsp);
11757c478bd9Sstevel@tonic-gate 			nfs4_async_manager_stop(vfsp);
11767c478bd9Sstevel@tonic-gate 			nfs4_remove_mi_from_server(mi, NULL);
117750a83466Sjwahlig 			if (rtvp != NULL)
11787c478bd9Sstevel@tonic-gate 				VN_RELE(rtvp);
117945916cd2Sjpk 			if (mntzone != NULL)
118045916cd2Sjpk 				zone_rele(mntzone);
118150a83466Sjwahlig 			/* need to remove it from the zone */
118250a83466Sjwahlig 			removed = nfs4_mi_zonelist_remove(mi);
118350a83466Sjwahlig 			if (removed)
1184a19609f8Sjv227347 				zone_rele_ref(&mi->mi_zone_ref,
1185a19609f8Sjv227347 				    ZONE_REF_NFSV4);
118650a83466Sjwahlig 			MI4_RELE(mi);
118739d3e169Sevanl 			if (!(uap->flags & MS_SYSSPACE) && args) {
118839d3e169Sevanl 				nfs4_free_args(args);
118939d3e169Sevanl 				kmem_free(args, sizeof (*args));
119039d3e169Sevanl 			}
11917c478bd9Sstevel@tonic-gate 			return (error);
11927c478bd9Sstevel@tonic-gate 		}
119350a83466Sjwahlig 		if (svp_head)
11947c478bd9Sstevel@tonic-gate 			sv4_free(svp_head);
11957c478bd9Sstevel@tonic-gate 	}
11967c478bd9Sstevel@tonic-gate 
119739d3e169Sevanl 	if (!(uap->flags & MS_SYSSPACE) && args) {
119839d3e169Sevanl 		nfs4_free_args(args);
119939d3e169Sevanl 		kmem_free(args, sizeof (*args));
120039d3e169Sevanl 	}
12017c478bd9Sstevel@tonic-gate 	if (rtvp != NULL)
12027c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
12037c478bd9Sstevel@tonic-gate 
120445916cd2Sjpk 	if (mntzone != NULL)
120545916cd2Sjpk 		zone_rele(mntzone);
120645916cd2Sjpk 
12077c478bd9Sstevel@tonic-gate 	return (error);
12087c478bd9Sstevel@tonic-gate }
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate #ifdef  DEBUG
12117c478bd9Sstevel@tonic-gate #define	VERS_MSG	"NFS4 server "
12127c478bd9Sstevel@tonic-gate #else
12137c478bd9Sstevel@tonic-gate #define	VERS_MSG	"NFS server "
12147c478bd9Sstevel@tonic-gate #endif
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate #define	READ_MSG        \
12177c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for read transfer size"
12187c478bd9Sstevel@tonic-gate #define	WRITE_MSG       \
12197c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for write transfer size"
12207c478bd9Sstevel@tonic-gate #define	SIZE_MSG        \
12217c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for maximum file size"
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate /*
12247c478bd9Sstevel@tonic-gate  * Get the symbolic link text from the server for a given filehandle
12257c478bd9Sstevel@tonic-gate  * of that symlink.
12267c478bd9Sstevel@tonic-gate  *
12277c478bd9Sstevel@tonic-gate  *      (get symlink text) PUTFH READLINK
12287c478bd9Sstevel@tonic-gate  */
12297c478bd9Sstevel@tonic-gate static int
getlinktext_otw(mntinfo4_t * mi,nfs_fh4 * fh,char ** linktextp,cred_t * cr,int flags)12307c478bd9Sstevel@tonic-gate getlinktext_otw(mntinfo4_t *mi, nfs_fh4 *fh, char **linktextp, cred_t *cr,
12317c478bd9Sstevel@tonic-gate     int flags)
12327c478bd9Sstevel@tonic-gate {
12337c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
12347c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
12357c478bd9Sstevel@tonic-gate 	int doqueue;
12367c478bd9Sstevel@tonic-gate 	nfs_argop4 argop[2];
12377c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
12387c478bd9Sstevel@tonic-gate 	READLINK4res *lr_res;
12397c478bd9Sstevel@tonic-gate 	uint_t len;
12407c478bd9Sstevel@tonic-gate 	bool_t needrecov = FALSE;
12417c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
12427c478bd9Sstevel@tonic-gate 	nfs4_sharedfh_t *sfh;
12437c478bd9Sstevel@tonic-gate 	nfs4_error_t e;
12447c478bd9Sstevel@tonic-gate 	int num_retry = nfs4_max_mount_retry;
12457c478bd9Sstevel@tonic-gate 	int recovery = !(flags & NFS4_GETFH_NEEDSOP);
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate 	sfh = sfh4_get(fh, mi);
12487c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
12497c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate recov_retry:
12527c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&e);
12537c478bd9Sstevel@tonic-gate 
12547c478bd9Sstevel@tonic-gate 	args.array_len = 2;
12557c478bd9Sstevel@tonic-gate 	args.array = argop;
12567c478bd9Sstevel@tonic-gate 	args.ctag = TAG_GET_SYMLINK;
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	if (! recovery) {
12597c478bd9Sstevel@tonic-gate 		e.error = nfs4_start_op(mi, NULL, NULL, &recov_state);
12607c478bd9Sstevel@tonic-gate 		if (e.error) {
12617c478bd9Sstevel@tonic-gate 			sfh4_rele(&sfh);
12627c478bd9Sstevel@tonic-gate 			return (e.error);
12637c478bd9Sstevel@tonic-gate 		}
12647c478bd9Sstevel@tonic-gate 	}
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate 	/* 0. putfh symlink fh */
12677c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_CPUTFH;
12687c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opcputfh.sfh = sfh;
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate 	/* 1. readlink */
12717c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_READLINK;
12727c478bd9Sstevel@tonic-gate 
12737c478bd9Sstevel@tonic-gate 	doqueue = 1;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, 0, &e);
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 	needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp);
12787c478bd9Sstevel@tonic-gate 
12797c478bd9Sstevel@tonic-gate 	if (needrecov && !recovery && num_retry-- > 0) {
12807c478bd9Sstevel@tonic-gate 
12817c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
12827c478bd9Sstevel@tonic-gate 		    "getlinktext_otw: initiating recovery\n"));
12837c478bd9Sstevel@tonic-gate 
12847c478bd9Sstevel@tonic-gate 		if (nfs4_start_recovery(&e, mi, NULL, NULL, NULL, NULL,
12852f172c55SRobert Thurlow 		    OP_READLINK, NULL, NULL, NULL) == FALSE) {
12867c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
12877c478bd9Sstevel@tonic-gate 			if (!e.error)
12887c478bd9Sstevel@tonic-gate 				(void) xdr_free(xdr_COMPOUND4res_clnt,
12897c478bd9Sstevel@tonic-gate 				    (caddr_t)&res);
12907c478bd9Sstevel@tonic-gate 			goto recov_retry;
12917c478bd9Sstevel@tonic-gate 		}
12927c478bd9Sstevel@tonic-gate 	}
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 	/*
12957c478bd9Sstevel@tonic-gate 	 * If non-NFS4 pcol error and/or we weren't able to recover.
12967c478bd9Sstevel@tonic-gate 	 */
12977c478bd9Sstevel@tonic-gate 	if (e.error != 0) {
12987c478bd9Sstevel@tonic-gate 		if (! recovery)
12997c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13007c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
13017c478bd9Sstevel@tonic-gate 		return (e.error);
13027c478bd9Sstevel@tonic-gate 	}
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 	if (res.status) {
13057c478bd9Sstevel@tonic-gate 		e.error = geterrno4(res.status);
13067c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
13077c478bd9Sstevel@tonic-gate 		if (! recovery)
13087c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13097c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
13107c478bd9Sstevel@tonic-gate 		return (e.error);
13117c478bd9Sstevel@tonic-gate 	}
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 	/* res.status == NFS4_OK */
13147c478bd9Sstevel@tonic-gate 	ASSERT(res.status == NFS4_OK);
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate 	resop = &res.array[1];  /* readlink res */
13177c478bd9Sstevel@tonic-gate 	lr_res = &resop->nfs_resop4_u.opreadlink;
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 	/* treat symlink name as data */
1320bb5aeaf5SMarcel Telka 	*linktextp = utf8_to_str((utf8string *)&lr_res->link, &len, NULL);
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate 	if (! recovery)
13237c478bd9Sstevel@tonic-gate 		nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13247c478bd9Sstevel@tonic-gate 	sfh4_rele(&sfh);
13257c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
13267c478bd9Sstevel@tonic-gate 	return (0);
13277c478bd9Sstevel@tonic-gate }
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate /*
13307c478bd9Sstevel@tonic-gate  * Skip over consecutive slashes and "/./" in a pathname.
13317c478bd9Sstevel@tonic-gate  */
13327c478bd9Sstevel@tonic-gate void
pathname_skipslashdot(struct pathname * pnp)13337c478bd9Sstevel@tonic-gate pathname_skipslashdot(struct pathname *pnp)
13347c478bd9Sstevel@tonic-gate {
13357c478bd9Sstevel@tonic-gate 	char *c1, *c2;
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate 	while (pnp->pn_pathlen > 0 && *pnp->pn_path == '/') {
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 		c1 = pnp->pn_path + 1;
13407c478bd9Sstevel@tonic-gate 		c2 = pnp->pn_path + 2;
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 		if (*c1 == '.' && (*c2 == '/' || *c2 == '\0')) {
13437c478bd9Sstevel@tonic-gate 			pnp->pn_path = pnp->pn_path + 2; /* skip "/." */
13447c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen = pnp->pn_pathlen - 2;
13457c478bd9Sstevel@tonic-gate 		} else {
13467c478bd9Sstevel@tonic-gate 			pnp->pn_path++;
13477c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen--;
13487c478bd9Sstevel@tonic-gate 		}
13497c478bd9Sstevel@tonic-gate 	}
13507c478bd9Sstevel@tonic-gate }
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate /*
13537c478bd9Sstevel@tonic-gate  * Resolve a symbolic link path. The symlink is in the nth component of
13547c478bd9Sstevel@tonic-gate  * svp->sv_path and has an nfs4 file handle "fh".
13557c478bd9Sstevel@tonic-gate  * Upon return, the sv_path will point to the new path that has the nth
13567c478bd9Sstevel@tonic-gate  * component resolved to its symlink text.
13577c478bd9Sstevel@tonic-gate  */
13587c478bd9Sstevel@tonic-gate int
resolve_sympath(mntinfo4_t * mi,servinfo4_t * svp,int nth,nfs_fh4 * fh,cred_t * cr,int flags)13597c478bd9Sstevel@tonic-gate resolve_sympath(mntinfo4_t *mi, servinfo4_t *svp, int nth, nfs_fh4 *fh,
13607c478bd9Sstevel@tonic-gate     cred_t *cr, int flags)
13617c478bd9Sstevel@tonic-gate {
13627c478bd9Sstevel@tonic-gate 	char *oldpath;
13637c478bd9Sstevel@tonic-gate 	char *symlink, *newpath;
13647c478bd9Sstevel@tonic-gate 	struct pathname oldpn, newpn;
13657c478bd9Sstevel@tonic-gate 	char component[MAXNAMELEN];
13667c478bd9Sstevel@tonic-gate 	int i, addlen, error = 0;
13677c478bd9Sstevel@tonic-gate 	int oldpathlen;
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate 	/* Get the symbolic link text over the wire. */
13707c478bd9Sstevel@tonic-gate 	error = getlinktext_otw(mi, fh, &symlink, cr, flags);
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate 	if (error || symlink == NULL || strlen(symlink) == 0)
13737c478bd9Sstevel@tonic-gate 		return (error);
13747c478bd9Sstevel@tonic-gate 
13757c478bd9Sstevel@tonic-gate 	/*
13767c478bd9Sstevel@tonic-gate 	 * Compose the new pathname.
13777c478bd9Sstevel@tonic-gate 	 * Note:
13787c478bd9Sstevel@tonic-gate 	 *    - only the nth component is resolved for the pathname.
13797c478bd9Sstevel@tonic-gate 	 *    - pathname.pn_pathlen does not count the ending null byte.
13807c478bd9Sstevel@tonic-gate 	 */
13817c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
13827c478bd9Sstevel@tonic-gate 	oldpath = svp->sv_path;
13837c478bd9Sstevel@tonic-gate 	oldpathlen = svp->sv_pathlen;
13847c478bd9Sstevel@tonic-gate 	if (error = pn_get(oldpath, UIO_SYSSPACE, &oldpn)) {
13857c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
13867c478bd9Sstevel@tonic-gate 		kmem_free(symlink, strlen(symlink) + 1);
13877c478bd9Sstevel@tonic-gate 		return (error);
13887c478bd9Sstevel@tonic-gate 	}
13897c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
13907c478bd9Sstevel@tonic-gate 	pn_alloc(&newpn);
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate 	/*
13937c478bd9Sstevel@tonic-gate 	 * Skip over previous components from the oldpath so that the
13947c478bd9Sstevel@tonic-gate 	 * oldpn.pn_path will point to the symlink component. Skip
13957c478bd9Sstevel@tonic-gate 	 * leading slashes and "/./" (no OP_LOOKUP on ".") so that
13967c478bd9Sstevel@tonic-gate 	 * pn_getcompnent can get the component.
13977c478bd9Sstevel@tonic-gate 	 */
13987c478bd9Sstevel@tonic-gate 	for (i = 1; i < nth; i++) {
13997c478bd9Sstevel@tonic-gate 		pathname_skipslashdot(&oldpn);
14007c478bd9Sstevel@tonic-gate 		error = pn_getcomponent(&oldpn, component);
14017c478bd9Sstevel@tonic-gate 		if (error)
14027c478bd9Sstevel@tonic-gate 			goto out;
14037c478bd9Sstevel@tonic-gate 	}
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 	/*
14067c478bd9Sstevel@tonic-gate 	 * Copy the old path upto the component right before the symlink
14077c478bd9Sstevel@tonic-gate 	 * if the symlink is not an absolute path.
14087c478bd9Sstevel@tonic-gate 	 */
14097c478bd9Sstevel@tonic-gate 	if (symlink[0] != '/') {
14107c478bd9Sstevel@tonic-gate 		addlen = oldpn.pn_path - oldpn.pn_buf;
14117c478bd9Sstevel@tonic-gate 		bcopy(oldpn.pn_buf, newpn.pn_path, addlen);
14127c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen += addlen;
14137c478bd9Sstevel@tonic-gate 		newpn.pn_path += addlen;
14147c478bd9Sstevel@tonic-gate 		newpn.pn_buf[newpn.pn_pathlen] = '/';
14157c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen++;
14167c478bd9Sstevel@tonic-gate 		newpn.pn_path++;
14177c478bd9Sstevel@tonic-gate 	}
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 	/* copy the resolved symbolic link text */
14207c478bd9Sstevel@tonic-gate 	addlen = strlen(symlink);
14217c478bd9Sstevel@tonic-gate 	if (newpn.pn_pathlen + addlen >= newpn.pn_bufsize) {
14227c478bd9Sstevel@tonic-gate 		error = ENAMETOOLONG;
14237c478bd9Sstevel@tonic-gate 		goto out;
14247c478bd9Sstevel@tonic-gate 	}
14257c478bd9Sstevel@tonic-gate 	bcopy(symlink, newpn.pn_path, addlen);
14267c478bd9Sstevel@tonic-gate 	newpn.pn_pathlen += addlen;
14277c478bd9Sstevel@tonic-gate 	newpn.pn_path += addlen;
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 	/*
14307c478bd9Sstevel@tonic-gate 	 * Check if there is any remaining path after the symlink component.
14317c478bd9Sstevel@tonic-gate 	 * First, skip the symlink component.
14327c478bd9Sstevel@tonic-gate 	 */
14337c478bd9Sstevel@tonic-gate 	pathname_skipslashdot(&oldpn);
14347c478bd9Sstevel@tonic-gate 	if (error = pn_getcomponent(&oldpn, component))
14357c478bd9Sstevel@tonic-gate 		goto out;
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate 	addlen = pn_pathleft(&oldpn); /* includes counting the slash */
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	/*
14407c478bd9Sstevel@tonic-gate 	 * Copy the remaining path to the new pathname if there is any.
14417c478bd9Sstevel@tonic-gate 	 */
14427c478bd9Sstevel@tonic-gate 	if (addlen > 0) {
14437c478bd9Sstevel@tonic-gate 		if (newpn.pn_pathlen + addlen >= newpn.pn_bufsize) {
14447c478bd9Sstevel@tonic-gate 			error = ENAMETOOLONG;
14457c478bd9Sstevel@tonic-gate 			goto out;
14467c478bd9Sstevel@tonic-gate 		}
14477c478bd9Sstevel@tonic-gate 		bcopy(oldpn.pn_path, newpn.pn_path, addlen);
14487c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen += addlen;
14497c478bd9Sstevel@tonic-gate 	}
14507c478bd9Sstevel@tonic-gate 	newpn.pn_buf[newpn.pn_pathlen] = '\0';
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	/* get the newpath and store it in the servinfo4_t */
14537c478bd9Sstevel@tonic-gate 	newpath = kmem_alloc(newpn.pn_pathlen + 1, KM_SLEEP);
14547c478bd9Sstevel@tonic-gate 	bcopy(newpn.pn_buf, newpath, newpn.pn_pathlen);
14557c478bd9Sstevel@tonic-gate 	newpath[newpn.pn_pathlen] = '\0';
14567c478bd9Sstevel@tonic-gate 
14577c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
14587c478bd9Sstevel@tonic-gate 	svp->sv_path = newpath;
14597c478bd9Sstevel@tonic-gate 	svp->sv_pathlen = strlen(newpath) + 1;
14607c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 	kmem_free(oldpath, oldpathlen);
14637c478bd9Sstevel@tonic-gate out:
14647c478bd9Sstevel@tonic-gate 	kmem_free(symlink, strlen(symlink) + 1);
14657c478bd9Sstevel@tonic-gate 	pn_free(&newpn);
14667c478bd9Sstevel@tonic-gate 	pn_free(&oldpn);
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 	return (error);
14697c478bd9Sstevel@tonic-gate }
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate /*
14722f172c55SRobert Thurlow  * This routine updates servinfo4 structure with the new referred server
14732f172c55SRobert Thurlow  * info.
14742f172c55SRobert Thurlow  * nfsfsloc has the location related information
14752f172c55SRobert Thurlow  * fsp has the hostname and pathname info.
14762f172c55SRobert Thurlow  * new path = pathname from referral + part of orig pathname(based on nth).
14772f172c55SRobert Thurlow  */
14782f172c55SRobert Thurlow static void
update_servinfo4(servinfo4_t * svp,fs_location4 * fsp,struct nfs_fsl_info * nfsfsloc,char * orig_path,int nth)14792f172c55SRobert Thurlow update_servinfo4(servinfo4_t *svp, fs_location4 *fsp,
14802f172c55SRobert Thurlow     struct nfs_fsl_info *nfsfsloc, char *orig_path, int nth)
14812f172c55SRobert Thurlow {
14822f172c55SRobert Thurlow 	struct knetconfig *knconf, *svknconf;
14832f172c55SRobert Thurlow 	struct netbuf *saddr;
14842f172c55SRobert Thurlow 	sec_data_t	*secdata;
14852f172c55SRobert Thurlow 	utf8string *host;
14862f172c55SRobert Thurlow 	int i = 0, num_slashes = 0;
14872f172c55SRobert Thurlow 	char *p, *spath, *op, *new_path;
14882f172c55SRobert Thurlow 
14892f172c55SRobert Thurlow 	/* Update knconf */
14902f172c55SRobert Thurlow 	knconf = svp->sv_knconf;
14912f172c55SRobert Thurlow 	free_knconf_contents(knconf);
14922f172c55SRobert Thurlow 	bzero(knconf, sizeof (struct knetconfig));
14932f172c55SRobert Thurlow 	svknconf = nfsfsloc->knconf;
14942f172c55SRobert Thurlow 	knconf->knc_semantics = svknconf->knc_semantics;
14952f172c55SRobert Thurlow 	knconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
14962f172c55SRobert Thurlow 	knconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
14972f172c55SRobert Thurlow 	knconf->knc_rdev = svknconf->knc_rdev;
14982f172c55SRobert Thurlow 	bcopy(svknconf->knc_protofmly, knconf->knc_protofmly, KNC_STRSIZE);
14992f172c55SRobert Thurlow 	bcopy(svknconf->knc_proto, knconf->knc_proto, KNC_STRSIZE);
15002f172c55SRobert Thurlow 
15012f172c55SRobert Thurlow 	/* Update server address */
15022f172c55SRobert Thurlow 	saddr = &svp->sv_addr;
15032f172c55SRobert Thurlow 	if (saddr->buf != NULL)
15042f172c55SRobert Thurlow 		kmem_free(saddr->buf, saddr->maxlen);
15052f172c55SRobert Thurlow 	saddr->buf  = kmem_alloc(nfsfsloc->addr->maxlen, KM_SLEEP);
15062f172c55SRobert Thurlow 	saddr->len = nfsfsloc->addr->len;
15072f172c55SRobert Thurlow 	saddr->maxlen = nfsfsloc->addr->maxlen;
15082f172c55SRobert Thurlow 	bcopy(nfsfsloc->addr->buf, saddr->buf, nfsfsloc->addr->len);
15092f172c55SRobert Thurlow 
15102f172c55SRobert Thurlow 	/* Update server name */
15112f172c55SRobert Thurlow 	host = fsp->server_val;
15122f172c55SRobert Thurlow 	kmem_free(svp->sv_hostname, svp->sv_hostnamelen);
15132f172c55SRobert Thurlow 	svp->sv_hostname = kmem_zalloc(host->utf8string_len + 1, KM_SLEEP);
15142f172c55SRobert Thurlow 	bcopy(host->utf8string_val, svp->sv_hostname, host->utf8string_len);
15152f172c55SRobert Thurlow 	svp->sv_hostname[host->utf8string_len] = '\0';
15162f172c55SRobert Thurlow 	svp->sv_hostnamelen = host->utf8string_len + 1;
15172f172c55SRobert Thurlow 
15182f172c55SRobert Thurlow 	/*
15192f172c55SRobert Thurlow 	 * Update server path.
15202f172c55SRobert Thurlow 	 * We need to setup proper path here.
15212f172c55SRobert Thurlow 	 * For ex., If we got a path name serv1:/rp/aaa/bbb
15222f172c55SRobert Thurlow 	 * where aaa is a referral and points to serv2:/rpool/aa
15232f172c55SRobert Thurlow 	 * we need to set the path to serv2:/rpool/aa/bbb
15242f172c55SRobert Thurlow 	 * The first part of this below code generates /rpool/aa
15252f172c55SRobert Thurlow 	 * and the second part appends /bbb to the server path.
15262f172c55SRobert Thurlow 	 */
15272f172c55SRobert Thurlow 	spath = p = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
15282f172c55SRobert Thurlow 	*p++ = '/';
15292f172c55SRobert Thurlow 	for (i = 0; i < fsp->rootpath.pathname4_len; i++) {
15302f172c55SRobert Thurlow 		component4 *comp;
15312f172c55SRobert Thurlow 
15322f172c55SRobert Thurlow 		comp = &fsp->rootpath.pathname4_val[i];
15332f172c55SRobert Thurlow 		/* If no space, null the string and bail */
15342f172c55SRobert Thurlow 		if ((p - spath) + comp->utf8string_len + 1 > MAXPATHLEN) {
15352f172c55SRobert Thurlow 			p = spath + MAXPATHLEN - 1;
15362f172c55SRobert Thurlow 			spath[0] = '\0';
15372f172c55SRobert Thurlow 			break;
15382f172c55SRobert Thurlow 		}
15392f172c55SRobert Thurlow 		bcopy(comp->utf8string_val, p, comp->utf8string_len);
15402f172c55SRobert Thurlow 		p += comp->utf8string_len;
15412f172c55SRobert Thurlow 		*p++ = '/';
15422f172c55SRobert Thurlow 	}
15432f172c55SRobert Thurlow 	if (fsp->rootpath.pathname4_len != 0)
15442f172c55SRobert Thurlow 		*(p - 1) = '\0';
15452f172c55SRobert Thurlow 	else
15462f172c55SRobert Thurlow 		*p = '\0';
15472f172c55SRobert Thurlow 	p = spath;
15482f172c55SRobert Thurlow 
15492f172c55SRobert Thurlow 	new_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
15502f172c55SRobert Thurlow 	(void) strlcpy(new_path, p, MAXPATHLEN);
15512f172c55SRobert Thurlow 	kmem_free(p, MAXPATHLEN);
15522f172c55SRobert Thurlow 	i = strlen(new_path);
15532f172c55SRobert Thurlow 
15542f172c55SRobert Thurlow 	for (op = orig_path; *op; op++) {
15552f172c55SRobert Thurlow 		if (*op == '/')
15562f172c55SRobert Thurlow 			num_slashes++;
15572f172c55SRobert Thurlow 		if (num_slashes == nth + 2) {
15582f172c55SRobert Thurlow 			while (*op != '\0') {
15592f172c55SRobert Thurlow 				new_path[i] = *op;
15602f172c55SRobert Thurlow 				i++;
15612f172c55SRobert Thurlow 				op++;
15622f172c55SRobert Thurlow 			}
15632f172c55SRobert Thurlow 			break;
15642f172c55SRobert Thurlow 		}
15652f172c55SRobert Thurlow 	}
15662f172c55SRobert Thurlow 	new_path[i] = '\0';
15672f172c55SRobert Thurlow 
15682f172c55SRobert Thurlow 	kmem_free(svp->sv_path, svp->sv_pathlen);
15692f172c55SRobert Thurlow 	svp->sv_pathlen = strlen(new_path) + 1;
15702f172c55SRobert Thurlow 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
15712f172c55SRobert Thurlow 	bcopy(new_path, svp->sv_path, svp->sv_pathlen);
15722f172c55SRobert Thurlow 	kmem_free(new_path, MAXPATHLEN);
15732f172c55SRobert Thurlow 
15742f172c55SRobert Thurlow 	/*
15752f172c55SRobert Thurlow 	 * All the security data is specific to old server.
15762f172c55SRobert Thurlow 	 * Clean it up except secdata which deals with mount options.
15772f172c55SRobert Thurlow 	 * We need to inherit that data. Copy secdata into our new servinfo4.
15782f172c55SRobert Thurlow 	 */
15792f172c55SRobert Thurlow 	if (svp->sv_dhsec) {
15802f172c55SRobert Thurlow 		sec_clnt_freeinfo(svp->sv_dhsec);
15812f172c55SRobert Thurlow 		svp->sv_dhsec = NULL;
15822f172c55SRobert Thurlow 	}
15832f172c55SRobert Thurlow 	if (svp->sv_save_secinfo &&
15842f172c55SRobert Thurlow 	    svp->sv_save_secinfo != svp->sv_secinfo) {
15852f172c55SRobert Thurlow 		secinfo_free(svp->sv_save_secinfo);
15862f172c55SRobert Thurlow 		svp->sv_save_secinfo = NULL;
15872f172c55SRobert Thurlow 	}
15882f172c55SRobert Thurlow 	if (svp->sv_secinfo) {
15892f172c55SRobert Thurlow 		secinfo_free(svp->sv_secinfo);
15902f172c55SRobert Thurlow 		svp->sv_secinfo = NULL;
15912f172c55SRobert Thurlow 	}
15922f172c55SRobert Thurlow 	svp->sv_currsec = NULL;
15932f172c55SRobert Thurlow 
15942f172c55SRobert Thurlow 	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
15952f172c55SRobert Thurlow 	*secdata = *svp->sv_secdata;
15962f172c55SRobert Thurlow 	secdata->data = NULL;
15972f172c55SRobert Thurlow 	if (svp->sv_secdata) {
15982f172c55SRobert Thurlow 		sec_clnt_freeinfo(svp->sv_secdata);
15992f172c55SRobert Thurlow 		svp->sv_secdata = NULL;
16002f172c55SRobert Thurlow 	}
16012f172c55SRobert Thurlow 	svp->sv_secdata = secdata;
16022f172c55SRobert Thurlow }
16032f172c55SRobert Thurlow 
16042f172c55SRobert Thurlow /*
16052f172c55SRobert Thurlow  * Resolve a referral. The referral is in the n+1th component of
16062f172c55SRobert Thurlow  * svp->sv_path and has a parent nfs4 file handle "fh".
16072f172c55SRobert Thurlow  * Upon return, the sv_path will point to the new path that has referral
16082f172c55SRobert Thurlow  * component resolved to its referred path and part of original path.
16092f172c55SRobert Thurlow  * Hostname and other address information is also updated.
16102f172c55SRobert Thurlow  */
16112f172c55SRobert Thurlow int
resolve_referral(mntinfo4_t * mi,servinfo4_t * svp,cred_t * cr,int nth,nfs_fh4 * fh)16122f172c55SRobert Thurlow resolve_referral(mntinfo4_t *mi, servinfo4_t *svp, cred_t *cr, int nth,
16132f172c55SRobert Thurlow     nfs_fh4 *fh)
16142f172c55SRobert Thurlow {
16152f172c55SRobert Thurlow 	nfs4_sharedfh_t	*sfh;
16162f172c55SRobert Thurlow 	struct nfs_fsl_info nfsfsloc;
16172f172c55SRobert Thurlow 	nfs4_ga_res_t garp;
16182f172c55SRobert Thurlow 	COMPOUND4res_clnt callres;
16192f172c55SRobert Thurlow 	fs_location4	*fsp;
16202f172c55SRobert Thurlow 	char *nm, *orig_path;
16212f172c55SRobert Thurlow 	int orig_pathlen = 0, ret = -1, index;
16222f172c55SRobert Thurlow 
16232f172c55SRobert Thurlow 	if (svp->sv_pathlen <= 0)
16242f172c55SRobert Thurlow 		return (ret);
16252f172c55SRobert Thurlow 
16262f172c55SRobert Thurlow 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
16272f172c55SRobert Thurlow 	orig_pathlen = svp->sv_pathlen;
16282f172c55SRobert Thurlow 	orig_path = kmem_alloc(orig_pathlen, KM_SLEEP);
16292f172c55SRobert Thurlow 	bcopy(svp->sv_path, orig_path, orig_pathlen);
16302f172c55SRobert Thurlow 	nm = extract_referral_point(svp->sv_path, nth);
16312f172c55SRobert Thurlow 	setup_newsvpath(svp, nth);
16322f172c55SRobert Thurlow 	nfs_rw_exit(&svp->sv_lock);
16332f172c55SRobert Thurlow 
16342f172c55SRobert Thurlow 	sfh = sfh4_get(fh, mi);
16352f172c55SRobert Thurlow 	index = nfs4_process_referral(mi, sfh, nm, cr,
16362f172c55SRobert Thurlow 	    &garp, &callres, &nfsfsloc);
16372f172c55SRobert Thurlow 	sfh4_rele(&sfh);
16382f172c55SRobert Thurlow 	kmem_free(nm, MAXPATHLEN);
16392f172c55SRobert Thurlow 	if (index < 0) {
16402f172c55SRobert Thurlow 		kmem_free(orig_path, orig_pathlen);
16412f172c55SRobert Thurlow 		return (index);
16422f172c55SRobert Thurlow 	}
16432f172c55SRobert Thurlow 
16442f172c55SRobert Thurlow 	fsp =  &garp.n4g_ext_res->n4g_fslocations.locations_val[index];
16452f172c55SRobert Thurlow 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
16462f172c55SRobert Thurlow 	update_servinfo4(svp, fsp, &nfsfsloc, orig_path, nth);
16472f172c55SRobert Thurlow 	nfs_rw_exit(&svp->sv_lock);
16482f172c55SRobert Thurlow 
16492f172c55SRobert Thurlow 	mutex_enter(&mi->mi_lock);
16502f172c55SRobert Thurlow 	mi->mi_vfs_referral_loop_cnt++;
16512f172c55SRobert Thurlow 	mutex_exit(&mi->mi_lock);
16522f172c55SRobert Thurlow 
16532f172c55SRobert Thurlow 	ret = 0;
16542f172c55SRobert Thurlow bad:
16552f172c55SRobert Thurlow 	/* Free up XDR memory allocated in nfs4_process_referral() */
16562f172c55SRobert Thurlow 	xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
16572f172c55SRobert Thurlow 	xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
16582f172c55SRobert Thurlow 	kmem_free(orig_path, orig_pathlen);
16592f172c55SRobert Thurlow 
16602f172c55SRobert Thurlow 	return (ret);
16612f172c55SRobert Thurlow }
16622f172c55SRobert Thurlow 
16632f172c55SRobert Thurlow /*
16647c478bd9Sstevel@tonic-gate  * Get the root filehandle for the given filesystem and server, and update
16657c478bd9Sstevel@tonic-gate  * svp.
16667c478bd9Sstevel@tonic-gate  *
16677c478bd9Sstevel@tonic-gate  * If NFS4_GETFH_NEEDSOP is set, then use nfs4_start_fop and nfs4_end_fop
16687c478bd9Sstevel@tonic-gate  * to coordinate with recovery.  Otherwise, the caller is assumed to be
16697c478bd9Sstevel@tonic-gate  * the recovery thread or have already done a start_fop.
16707c478bd9Sstevel@tonic-gate  *
16717c478bd9Sstevel@tonic-gate  * Errors are returned by the nfs4_error_t parameter.
16727c478bd9Sstevel@tonic-gate  */
16737c478bd9Sstevel@tonic-gate static void
nfs4getfh_otw(struct mntinfo4 * mi,servinfo4_t * svp,vtype_t * vtp,int flags,cred_t * cr,nfs4_error_t * ep)16747c478bd9Sstevel@tonic-gate nfs4getfh_otw(struct mntinfo4 *mi, servinfo4_t *svp, vtype_t *vtp,
16757c478bd9Sstevel@tonic-gate     int flags, cred_t *cr, nfs4_error_t *ep)
16767c478bd9Sstevel@tonic-gate {
16777c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
16787c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
16797c478bd9Sstevel@tonic-gate 	int doqueue = 1;
16807c478bd9Sstevel@tonic-gate 	nfs_argop4 *argop;
16817c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
16827c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t *garp;
16837c478bd9Sstevel@tonic-gate 	int num_argops;
16847c478bd9Sstevel@tonic-gate 	lookup4_param_t lookuparg;
16857c478bd9Sstevel@tonic-gate 	nfs_fh4 *tmpfhp;
16867c478bd9Sstevel@tonic-gate 	nfs_fh4 *resfhp;
16877c478bd9Sstevel@tonic-gate 	bool_t needrecov = FALSE;
16887c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
16897c478bd9Sstevel@tonic-gate 	int llndx;
16907c478bd9Sstevel@tonic-gate 	int nthcomp;
16917c478bd9Sstevel@tonic-gate 	int recovery = !(flags & NFS4_GETFH_NEEDSOP);
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
16947c478bd9Sstevel@tonic-gate 	ASSERT(svp->sv_path != NULL);
16957c478bd9Sstevel@tonic-gate 	if (svp->sv_path[0] == '\0') {
16967c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
16977c478bd9Sstevel@tonic-gate 		nfs4_error_init(ep, EINVAL);
16987c478bd9Sstevel@tonic-gate 		return;
16997c478bd9Sstevel@tonic-gate 	}
17007c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
17017c478bd9Sstevel@tonic-gate 
17027c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
17037c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
17042f172c55SRobert Thurlow 
17057c478bd9Sstevel@tonic-gate recov_retry:
17062f172c55SRobert Thurlow 	if (mi->mi_vfs_referral_loop_cnt >= NFS4_REFERRAL_LOOP_MAX) {
17072f172c55SRobert Thurlow 		DTRACE_PROBE3(nfs4clnt__debug__referral__loop, mntinfo4 *,
17082f172c55SRobert Thurlow 		    mi, servinfo4_t *, svp, char *, "nfs4getfh_otw");
17092f172c55SRobert Thurlow 		nfs4_error_init(ep, EINVAL);
17102f172c55SRobert Thurlow 		return;
17112f172c55SRobert Thurlow 	}
17127c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(ep);
17137c478bd9Sstevel@tonic-gate 
17147c478bd9Sstevel@tonic-gate 	if (!recovery) {
17157c478bd9Sstevel@tonic-gate 		ep->error = nfs4_start_fop(mi, NULL, NULL, OH_MOUNT,
17167c478bd9Sstevel@tonic-gate 		    &recov_state, NULL);
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 		/*
17197c478bd9Sstevel@tonic-gate 		 * If recovery has been started and this request as
17207c478bd9Sstevel@tonic-gate 		 * initiated by a mount, then we must wait for recovery
17217c478bd9Sstevel@tonic-gate 		 * to finish before proceeding, otherwise, the error
17227c478bd9Sstevel@tonic-gate 		 * cleanup would remove data structures needed by the
17237c478bd9Sstevel@tonic-gate 		 * recovery thread.
17247c478bd9Sstevel@tonic-gate 		 */
17257c478bd9Sstevel@tonic-gate 		if (ep->error) {
17267c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
17277c478bd9Sstevel@tonic-gate 			if (mi->mi_flags & MI4_MOUNTING) {
17287c478bd9Sstevel@tonic-gate 				mi->mi_flags |= MI4_RECOV_FAIL;
17297c478bd9Sstevel@tonic-gate 				mi->mi_error = EIO;
17307c478bd9Sstevel@tonic-gate 
17317c478bd9Sstevel@tonic-gate 				NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
17327c478bd9Sstevel@tonic-gate 				    "nfs4getfh_otw: waiting 4 recovery\n"));
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 				while (mi->mi_flags & MI4_RECOV_ACTIV)
17357c478bd9Sstevel@tonic-gate 					cv_wait(&mi->mi_failover_cv,
17367c478bd9Sstevel@tonic-gate 					    &mi->mi_lock);
17377c478bd9Sstevel@tonic-gate 			}
17387c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
17397c478bd9Sstevel@tonic-gate 			return;
17407c478bd9Sstevel@tonic-gate 		}
17417c478bd9Sstevel@tonic-gate 
17427c478bd9Sstevel@tonic-gate 		/*
17437c478bd9Sstevel@tonic-gate 		 * If the client does not specify a specific flavor to use
17447c478bd9Sstevel@tonic-gate 		 * and has not gotten a secinfo list from the server yet,
17457c478bd9Sstevel@tonic-gate 		 * retrieve the secinfo list from the server and use a
17467c478bd9Sstevel@tonic-gate 		 * flavor from the list to mount.
17477c478bd9Sstevel@tonic-gate 		 *
17487c478bd9Sstevel@tonic-gate 		 * If fail to get the secinfo list from the server, then
17497c478bd9Sstevel@tonic-gate 		 * try the default flavor.
17507c478bd9Sstevel@tonic-gate 		 */
17517c478bd9Sstevel@tonic-gate 		if ((svp->sv_flags & SV4_TRYSECDEFAULT) &&
17527c478bd9Sstevel@tonic-gate 		    svp->sv_secinfo == NULL) {
17537c478bd9Sstevel@tonic-gate 			(void) nfs4_secinfo_path(mi, cr, FALSE);
17547c478bd9Sstevel@tonic-gate 		}
17557c478bd9Sstevel@tonic-gate 	}
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 	if (recovery)
17587c478bd9Sstevel@tonic-gate 		args.ctag = TAG_REMAP_MOUNT;
17597c478bd9Sstevel@tonic-gate 	else
17607c478bd9Sstevel@tonic-gate 		args.ctag = TAG_MOUNT;
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 	lookuparg.l4_getattrs = LKP4_ALL_ATTRIBUTES;
17637c478bd9Sstevel@tonic-gate 	lookuparg.argsp = &args;
17647c478bd9Sstevel@tonic-gate 	lookuparg.resp = &res;
17657c478bd9Sstevel@tonic-gate 	lookuparg.header_len = 2;	/* Putrootfh, getfh */
17667c478bd9Sstevel@tonic-gate 	lookuparg.trailer_len = 0;
17677c478bd9Sstevel@tonic-gate 	lookuparg.ga_bits = FATTR4_FSINFO_MASK;
17687c478bd9Sstevel@tonic-gate 	lookuparg.mi = mi;
17697c478bd9Sstevel@tonic-gate 
17707c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
17717c478bd9Sstevel@tonic-gate 	ASSERT(svp->sv_path != NULL);
17727c478bd9Sstevel@tonic-gate 	llndx = nfs4lookup_setup(svp->sv_path, &lookuparg, 0);
17737c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 	argop = args.array;
17767c478bd9Sstevel@tonic-gate 	num_argops = args.array_len;
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate 	/* choose public or root filehandle */
17797c478bd9Sstevel@tonic-gate 	if (flags & NFS4_GETFH_PUBLIC)
17807c478bd9Sstevel@tonic-gate 		argop[0].argop = OP_PUTPUBFH;
17817c478bd9Sstevel@tonic-gate 	else
17827c478bd9Sstevel@tonic-gate 		argop[0].argop = OP_PUTROOTFH;
17837c478bd9Sstevel@tonic-gate 
17847c478bd9Sstevel@tonic-gate 	/* get fh */
17857c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_GETFH;
17867c478bd9Sstevel@tonic-gate 
17877c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE,
17887c478bd9Sstevel@tonic-gate 	    "nfs4getfh_otw: %s call, mi 0x%p",
17897c478bd9Sstevel@tonic-gate 	    needrecov ? "recov" : "first", (void *)mi));
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, ep);
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 	needrecov = nfs4_needs_recovery(ep, FALSE, mi->mi_vfsp);
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 	if (needrecov) {
17967c478bd9Sstevel@tonic-gate 		bool_t abort;
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 		if (recovery) {
17997c478bd9Sstevel@tonic-gate 			nfs4args_lookup_free(argop, num_argops);
18007c478bd9Sstevel@tonic-gate 			kmem_free(argop,
18017c478bd9Sstevel@tonic-gate 			    lookuparg.arglen * sizeof (nfs_argop4));
18027c478bd9Sstevel@tonic-gate 			if (!ep->error)
18037c478bd9Sstevel@tonic-gate 				(void) xdr_free(xdr_COMPOUND4res_clnt,
18047c478bd9Sstevel@tonic-gate 				    (caddr_t)&res);
18057c478bd9Sstevel@tonic-gate 			return;
18067c478bd9Sstevel@tonic-gate 		}
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_recov_debug,
18097c478bd9Sstevel@tonic-gate 		    (CE_NOTE, "nfs4getfh_otw: initiating recovery\n"));
18107c478bd9Sstevel@tonic-gate 
18117c478bd9Sstevel@tonic-gate 		abort = nfs4_start_recovery(ep, mi, NULL,
18122f172c55SRobert Thurlow 		    NULL, NULL, NULL, OP_GETFH, NULL, NULL, NULL);
18137c478bd9Sstevel@tonic-gate 		if (!ep->error) {
18147c478bd9Sstevel@tonic-gate 			ep->error = geterrno4(res.status);
18157c478bd9Sstevel@tonic-gate 			(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
18167c478bd9Sstevel@tonic-gate 		}
18177c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18187c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18197c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
18207c478bd9Sstevel@tonic-gate 		/* have another go? */
18217c478bd9Sstevel@tonic-gate 		if (abort == FALSE)
18227c478bd9Sstevel@tonic-gate 			goto recov_retry;
18237c478bd9Sstevel@tonic-gate 		return;
18247c478bd9Sstevel@tonic-gate 	}
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate 	/*
18277c478bd9Sstevel@tonic-gate 	 * No recovery, but check if error is set.
18287c478bd9Sstevel@tonic-gate 	 */
18297c478bd9Sstevel@tonic-gate 	if (ep->error)  {
18307c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18317c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18327c478bd9Sstevel@tonic-gate 		if (!recovery)
18337c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
18347c478bd9Sstevel@tonic-gate 			    needrecov);
18357c478bd9Sstevel@tonic-gate 		return;
18367c478bd9Sstevel@tonic-gate 	}
18377c478bd9Sstevel@tonic-gate 
18387c478bd9Sstevel@tonic-gate is_link_err:
18397c478bd9Sstevel@tonic-gate 
18407c478bd9Sstevel@tonic-gate 	/* for non-recovery errors */
18412f172c55SRobert Thurlow 	if (res.status && res.status != NFS4ERR_SYMLINK &&
18422f172c55SRobert Thurlow 	    res.status != NFS4ERR_MOVED) {
18437c478bd9Sstevel@tonic-gate 		if (!recovery) {
18447c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
18457c478bd9Sstevel@tonic-gate 			    needrecov);
18467c478bd9Sstevel@tonic-gate 		}
18477c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18487c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18497c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
18507c478bd9Sstevel@tonic-gate 		return;
18517c478bd9Sstevel@tonic-gate 	}
18527c478bd9Sstevel@tonic-gate 
18537c478bd9Sstevel@tonic-gate 	/*
18547c478bd9Sstevel@tonic-gate 	 * If any intermediate component in the path is a symbolic link,
18557c478bd9Sstevel@tonic-gate 	 * resolve the symlink, then try mount again using the new path.
18567c478bd9Sstevel@tonic-gate 	 */
18572f172c55SRobert Thurlow 	if (res.status == NFS4ERR_SYMLINK || res.status == NFS4ERR_MOVED) {
18587c478bd9Sstevel@tonic-gate 		int where;
18597c478bd9Sstevel@tonic-gate 
18607c478bd9Sstevel@tonic-gate 		/*
18612f172c55SRobert Thurlow 		 * Need to call nfs4_end_op before resolve_sympath to avoid
18622f172c55SRobert Thurlow 		 * potential nfs4_start_op deadlock.
18632f172c55SRobert Thurlow 		 */
18642f172c55SRobert Thurlow 		if (!recovery)
18652f172c55SRobert Thurlow 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
18662f172c55SRobert Thurlow 			    needrecov);
18672f172c55SRobert Thurlow 
18682f172c55SRobert Thurlow 		/*
18697c478bd9Sstevel@tonic-gate 		 * This must be from OP_LOOKUP failure. The (cfh) for this
18707c478bd9Sstevel@tonic-gate 		 * OP_LOOKUP is a symlink node. Found out where the
18717c478bd9Sstevel@tonic-gate 		 * OP_GETFH is for the (cfh) that is a symlink node.
18727c478bd9Sstevel@tonic-gate 		 *
18737c478bd9Sstevel@tonic-gate 		 * Example:
18747c478bd9Sstevel@tonic-gate 		 * (mount) PUTROOTFH, GETFH, LOOKUP comp1, GETFH, GETATTR,
18757c478bd9Sstevel@tonic-gate 		 * LOOKUP comp2, GETFH, GETATTR, LOOKUP comp3, GETFH, GETATTR
18767c478bd9Sstevel@tonic-gate 		 *
18777c478bd9Sstevel@tonic-gate 		 * LOOKUP comp3 fails with SYMLINK because comp2 is a symlink.
18787c478bd9Sstevel@tonic-gate 		 * In this case, where = 7, nthcomp = 2.
18797c478bd9Sstevel@tonic-gate 		 */
18807c478bd9Sstevel@tonic-gate 		where = res.array_len - 2;
18817c478bd9Sstevel@tonic-gate 		ASSERT(where > 0);
18827c478bd9Sstevel@tonic-gate 
18832f172c55SRobert Thurlow 		if (res.status == NFS4ERR_SYMLINK) {
18842f172c55SRobert Thurlow 
18857c478bd9Sstevel@tonic-gate 			resop = &res.array[where - 1];
18867c478bd9Sstevel@tonic-gate 			ASSERT(resop->resop == OP_GETFH);
18877c478bd9Sstevel@tonic-gate 			tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
18887c478bd9Sstevel@tonic-gate 			nthcomp = res.array_len/3 - 1;
18892f172c55SRobert Thurlow 			ep->error = resolve_sympath(mi, svp, nthcomp,
18902f172c55SRobert Thurlow 			    tmpfhp, cr, flags);
18917c478bd9Sstevel@tonic-gate 
18922f172c55SRobert Thurlow 		} else if (res.status == NFS4ERR_MOVED) {
18937c478bd9Sstevel@tonic-gate 
18942f172c55SRobert Thurlow 			resop = &res.array[where - 2];
18952f172c55SRobert Thurlow 			ASSERT(resop->resop == OP_GETFH);
18962f172c55SRobert Thurlow 			tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
18972f172c55SRobert Thurlow 			nthcomp = res.array_len/3 - 1;
18982f172c55SRobert Thurlow 			ep->error = resolve_referral(mi, svp, cr, nthcomp,
18992f172c55SRobert Thurlow 			    tmpfhp);
19002f172c55SRobert Thurlow 		}
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
19037c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
19047c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
19057c478bd9Sstevel@tonic-gate 
19067c478bd9Sstevel@tonic-gate 		if (ep->error)
19077c478bd9Sstevel@tonic-gate 			return;
19087c478bd9Sstevel@tonic-gate 
19097c478bd9Sstevel@tonic-gate 		goto recov_retry;
19107c478bd9Sstevel@tonic-gate 	}
19117c478bd9Sstevel@tonic-gate 
19127c478bd9Sstevel@tonic-gate 	/* getfh */
19137c478bd9Sstevel@tonic-gate 	resop = &res.array[res.array_len - 2];
19147c478bd9Sstevel@tonic-gate 	ASSERT(resop->resop == OP_GETFH);
19157c478bd9Sstevel@tonic-gate 	resfhp = &resop->nfs_resop4_u.opgetfh.object;
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	/* getattr fsinfo res */
19187c478bd9Sstevel@tonic-gate 	resop++;
19197c478bd9Sstevel@tonic-gate 	garp = &resop->nfs_resop4_u.opgetattr.ga_res;
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 	*vtp = garp->n4g_va.va_type;
19227c478bd9Sstevel@tonic-gate 
19237c478bd9Sstevel@tonic-gate 	mi->mi_fh_expire_type = garp->n4g_ext_res->n4g_fet;
19247c478bd9Sstevel@tonic-gate 
19257c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
19267c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_pc4.pc4_link_support)
19277c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_LINK;
19287c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_pc4.pc4_symlink_support)
19297c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_SYMLINK;
19307c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_suppattrs & FATTR4_ACL_MASK)
19317c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_ACL;
19327c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxread == 0)
19357c478bd9Sstevel@tonic-gate 		mi->mi_tsize =
19367c478bd9Sstevel@tonic-gate 		    MIN(MAXBSIZE, mi->mi_tsize);
19377c478bd9Sstevel@tonic-gate 	else
19387c478bd9Sstevel@tonic-gate 		mi->mi_tsize =
19397c478bd9Sstevel@tonic-gate 		    MIN(garp->n4g_ext_res->n4g_maxread,
19407c478bd9Sstevel@tonic-gate 		    mi->mi_tsize);
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxwrite == 0)
19437c478bd9Sstevel@tonic-gate 		mi->mi_stsize =
19447c478bd9Sstevel@tonic-gate 		    MIN(MAXBSIZE, mi->mi_stsize);
19457c478bd9Sstevel@tonic-gate 	else
19467c478bd9Sstevel@tonic-gate 		mi->mi_stsize =
19477c478bd9Sstevel@tonic-gate 		    MIN(garp->n4g_ext_res->n4g_maxwrite,
19487c478bd9Sstevel@tonic-gate 		    mi->mi_stsize);
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxfilesize != 0)
19517c478bd9Sstevel@tonic-gate 		mi->mi_maxfilesize =
19527c478bd9Sstevel@tonic-gate 		    MIN(garp->n4g_ext_res->n4g_maxfilesize,
19537c478bd9Sstevel@tonic-gate 		    mi->mi_maxfilesize);
19547c478bd9Sstevel@tonic-gate 
19557c478bd9Sstevel@tonic-gate 	/*
19567c478bd9Sstevel@tonic-gate 	 * If the final component is a a symbolic link, resolve the symlink,
19577c478bd9Sstevel@tonic-gate 	 * then try mount again using the new path.
19587c478bd9Sstevel@tonic-gate 	 *
19597c478bd9Sstevel@tonic-gate 	 * Assume no symbolic link for root filesysm "/".
19607c478bd9Sstevel@tonic-gate 	 */
19617c478bd9Sstevel@tonic-gate 	if (*vtp == VLNK) {
19627c478bd9Sstevel@tonic-gate 		/*
19637c478bd9Sstevel@tonic-gate 		 * nthcomp is the total result length minus
19647c478bd9Sstevel@tonic-gate 		 * the 1st 2 OPs (PUTROOTFH, GETFH),
19657c478bd9Sstevel@tonic-gate 		 * then divided by 3 (LOOKUP,GETFH,GETATTR)
19667c478bd9Sstevel@tonic-gate 		 *
19677c478bd9Sstevel@tonic-gate 		 * e.g. PUTROOTFH GETFH LOOKUP 1st-comp GETFH GETATTR
19687c478bd9Sstevel@tonic-gate 		 *	LOOKUP 2nd-comp GETFH GETATTR
19697c478bd9Sstevel@tonic-gate 		 *
19707c478bd9Sstevel@tonic-gate 		 *	(8 - 2)/3 = 2
19717c478bd9Sstevel@tonic-gate 		 */
19727c478bd9Sstevel@tonic-gate 		nthcomp = (res.array_len - 2)/3;
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate 		/*
19757c478bd9Sstevel@tonic-gate 		 * Need to call nfs4_end_op before resolve_sympath to avoid
19767c478bd9Sstevel@tonic-gate 		 * potential nfs4_start_op deadlock. See RFE 4777612.
19777c478bd9Sstevel@tonic-gate 		 */
19787c478bd9Sstevel@tonic-gate 		if (!recovery)
19797c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
19807c478bd9Sstevel@tonic-gate 			    needrecov);
19817c478bd9Sstevel@tonic-gate 
19827c478bd9Sstevel@tonic-gate 		ep->error = resolve_sympath(mi, svp, nthcomp, resfhp, cr,
19837c478bd9Sstevel@tonic-gate 		    flags);
19847c478bd9Sstevel@tonic-gate 
19857c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
19867c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
19877c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 		if (ep->error)
19907c478bd9Sstevel@tonic-gate 			return;
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 		goto recov_retry;
19937c478bd9Sstevel@tonic-gate 	}
19947c478bd9Sstevel@tonic-gate 
19957c478bd9Sstevel@tonic-gate 	/*
19967c478bd9Sstevel@tonic-gate 	 * We need to figure out where in the compound the getfh
19977c478bd9Sstevel@tonic-gate 	 * for the parent directory is. If the object to be mounted is
19987c478bd9Sstevel@tonic-gate 	 * the root, then there is no lookup at all:
19997c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH.
20007c478bd9Sstevel@tonic-gate 	 * If the object to be mounted is in the root, then the compound is:
20017c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH, LOOKUP, GETFH, GETATTR.
20027c478bd9Sstevel@tonic-gate 	 * In either of these cases, the index of the GETFH is 1.
20037c478bd9Sstevel@tonic-gate 	 * If it is not at the root, then it's something like:
20047c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH, LOOKUP, GETFH, GETATTR,
20057c478bd9Sstevel@tonic-gate 	 * LOOKUP, GETFH, GETATTR
20067c478bd9Sstevel@tonic-gate 	 * In this case, the index is llndx (last lookup index) - 2.
20077c478bd9Sstevel@tonic-gate 	 */
20087c478bd9Sstevel@tonic-gate 	if (llndx == -1 || llndx == 2)
20097c478bd9Sstevel@tonic-gate 		resop = &res.array[1];
20107c478bd9Sstevel@tonic-gate 	else {
20117c478bd9Sstevel@tonic-gate 		ASSERT(llndx > 2);
20127c478bd9Sstevel@tonic-gate 		resop = &res.array[llndx-2];
20137c478bd9Sstevel@tonic-gate 	}
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 	ASSERT(resop->resop == OP_GETFH);
20167c478bd9Sstevel@tonic-gate 	tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	/* save the filehandles for the replica */
20197c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
20207c478bd9Sstevel@tonic-gate 	ASSERT(tmpfhp->nfs_fh4_len <= NFS4_FHSIZE);
20217c478bd9Sstevel@tonic-gate 	svp->sv_pfhandle.fh_len = tmpfhp->nfs_fh4_len;
20227c478bd9Sstevel@tonic-gate 	bcopy(tmpfhp->nfs_fh4_val, svp->sv_pfhandle.fh_buf,
20237c478bd9Sstevel@tonic-gate 	    tmpfhp->nfs_fh4_len);
20247c478bd9Sstevel@tonic-gate 	ASSERT(resfhp->nfs_fh4_len <= NFS4_FHSIZE);
20257c478bd9Sstevel@tonic-gate 	svp->sv_fhandle.fh_len = resfhp->nfs_fh4_len;
20267c478bd9Sstevel@tonic-gate 	bcopy(resfhp->nfs_fh4_val, svp->sv_fhandle.fh_buf, resfhp->nfs_fh4_len);
20277c478bd9Sstevel@tonic-gate 
20287c478bd9Sstevel@tonic-gate 	/* initialize fsid and supp_attrs for server fs */
20297c478bd9Sstevel@tonic-gate 	svp->sv_fsid = garp->n4g_fsid;
20307c478bd9Sstevel@tonic-gate 	svp->sv_supp_attrs =
20317c478bd9Sstevel@tonic-gate 	    garp->n4g_ext_res->n4g_suppattrs | FATTR4_MANDATTR_MASK;
20327c478bd9Sstevel@tonic-gate 
20337c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
20347c478bd9Sstevel@tonic-gate 	nfs4args_lookup_free(argop, num_argops);
20357c478bd9Sstevel@tonic-gate 	kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
20367c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
20377c478bd9Sstevel@tonic-gate 	if (!recovery)
20387c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
20397c478bd9Sstevel@tonic-gate }
20407c478bd9Sstevel@tonic-gate 
20412f172c55SRobert Thurlow /*
20422f172c55SRobert Thurlow  * Save a copy of Servinfo4_t structure.
20432f172c55SRobert Thurlow  * We might need when there is a failure in getting file handle
20442f172c55SRobert Thurlow  * in case of a referral to replace servinfo4 struct and try again.
20452f172c55SRobert Thurlow  */
20462f172c55SRobert Thurlow static struct servinfo4 *
copy_svp(servinfo4_t * nsvp)20472f172c55SRobert Thurlow copy_svp(servinfo4_t *nsvp)
20482f172c55SRobert Thurlow {
20492f172c55SRobert Thurlow 	servinfo4_t *svp = NULL;
20502f172c55SRobert Thurlow 	struct knetconfig *sknconf, *tknconf;
20512f172c55SRobert Thurlow 	struct netbuf *saddr, *taddr;
20522f172c55SRobert Thurlow 
20532f172c55SRobert Thurlow 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
20542f172c55SRobert Thurlow 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
20552f172c55SRobert Thurlow 	svp->sv_flags = nsvp->sv_flags;
20562f172c55SRobert Thurlow 	svp->sv_fsid = nsvp->sv_fsid;
20572f172c55SRobert Thurlow 	svp->sv_hostnamelen = nsvp->sv_hostnamelen;
20582f172c55SRobert Thurlow 	svp->sv_pathlen = nsvp->sv_pathlen;
20592f172c55SRobert Thurlow 	svp->sv_supp_attrs = nsvp->sv_supp_attrs;
20602f172c55SRobert Thurlow 
20612f172c55SRobert Thurlow 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
20622f172c55SRobert Thurlow 	svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP);
20632f172c55SRobert Thurlow 	bcopy(nsvp->sv_hostname, svp->sv_hostname, svp->sv_hostnamelen);
20642f172c55SRobert Thurlow 	bcopy(nsvp->sv_path, svp->sv_path, svp->sv_pathlen);
20652f172c55SRobert Thurlow 
20662f172c55SRobert Thurlow 	saddr = &nsvp->sv_addr;
20672f172c55SRobert Thurlow 	taddr = &svp->sv_addr;
20682f172c55SRobert Thurlow 	taddr->maxlen = saddr->maxlen;
20692f172c55SRobert Thurlow 	taddr->len = saddr->len;
20702f172c55SRobert Thurlow 	if (saddr->len > 0) {
20712f172c55SRobert Thurlow 		taddr->buf = kmem_zalloc(saddr->maxlen, KM_SLEEP);
20722f172c55SRobert Thurlow 		bcopy(saddr->buf, taddr->buf, saddr->len);
20732f172c55SRobert Thurlow 	}
20742f172c55SRobert Thurlow 
20752f172c55SRobert Thurlow 	svp->sv_knconf = kmem_zalloc(sizeof (struct knetconfig), KM_SLEEP);
20762f172c55SRobert Thurlow 	sknconf = nsvp->sv_knconf;
20772f172c55SRobert Thurlow 	tknconf = svp->sv_knconf;
20782f172c55SRobert Thurlow 	tknconf->knc_semantics = sknconf->knc_semantics;
20792f172c55SRobert Thurlow 	tknconf->knc_rdev = sknconf->knc_rdev;
20802f172c55SRobert Thurlow 	if (sknconf->knc_proto != NULL) {
20812f172c55SRobert Thurlow 		tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
20822f172c55SRobert Thurlow 		bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto,
20832f172c55SRobert Thurlow 		    KNC_STRSIZE);
20842f172c55SRobert Thurlow 	}
20852f172c55SRobert Thurlow 	if (sknconf->knc_protofmly != NULL) {
20862f172c55SRobert Thurlow 		tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
20872f172c55SRobert Thurlow 		bcopy(sknconf->knc_protofmly, (char *)tknconf->knc_protofmly,
20882f172c55SRobert Thurlow 		    KNC_STRSIZE);
20892f172c55SRobert Thurlow 	}
20902f172c55SRobert Thurlow 
20912f172c55SRobert Thurlow 	if (nsvp->sv_origknconf != NULL) {
20922f172c55SRobert Thurlow 		svp->sv_origknconf = kmem_zalloc(sizeof (struct knetconfig),
20932f172c55SRobert Thurlow 		    KM_SLEEP);
20942f172c55SRobert Thurlow 		sknconf = nsvp->sv_origknconf;
20952f172c55SRobert Thurlow 		tknconf = svp->sv_origknconf;
20962f172c55SRobert Thurlow 		tknconf->knc_semantics = sknconf->knc_semantics;
20972f172c55SRobert Thurlow 		tknconf->knc_rdev = sknconf->knc_rdev;
20982f172c55SRobert Thurlow 		if (sknconf->knc_proto != NULL) {
20992f172c55SRobert Thurlow 			tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
21002f172c55SRobert Thurlow 			bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto,
21012f172c55SRobert Thurlow 			    KNC_STRSIZE);
21022f172c55SRobert Thurlow 		}
21032f172c55SRobert Thurlow 		if (sknconf->knc_protofmly != NULL) {
21042f172c55SRobert Thurlow 			tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE,
21052f172c55SRobert Thurlow 			    KM_SLEEP);
21062f172c55SRobert Thurlow 			bcopy(sknconf->knc_protofmly,
21072f172c55SRobert Thurlow 			    (char *)tknconf->knc_protofmly, KNC_STRSIZE);
21082f172c55SRobert Thurlow 		}
21092f172c55SRobert Thurlow 	}
21102f172c55SRobert Thurlow 
21112f172c55SRobert Thurlow 	svp->sv_secdata = copy_sec_data(nsvp->sv_secdata);
21122f172c55SRobert Thurlow 	svp->sv_dhsec = copy_sec_data(svp->sv_dhsec);
21132f172c55SRobert Thurlow 	/*
21142f172c55SRobert Thurlow 	 * Rest of the security information is not copied as they are built
21152f172c55SRobert Thurlow 	 * with the information available from secdata and dhsec.
21162f172c55SRobert Thurlow 	 */
21172f172c55SRobert Thurlow 	svp->sv_next = NULL;
21182f172c55SRobert Thurlow 
21192f172c55SRobert Thurlow 	return (svp);
21202f172c55SRobert Thurlow }
21212f172c55SRobert Thurlow 
21222f172c55SRobert Thurlow servinfo4_t *
restore_svp(mntinfo4_t * mi,servinfo4_t * svp,servinfo4_t * origsvp)21232f172c55SRobert Thurlow restore_svp(mntinfo4_t *mi, servinfo4_t *svp, servinfo4_t *origsvp)
21242f172c55SRobert Thurlow {
21252f172c55SRobert Thurlow 	servinfo4_t *srvnext, *tmpsrv;
21262f172c55SRobert Thurlow 
21272f172c55SRobert Thurlow 	if (strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) {
21282f172c55SRobert Thurlow 		/*
21292f172c55SRobert Thurlow 		 * Since the hostname changed, we must be dealing
21302f172c55SRobert Thurlow 		 * with a referral, and the lookup failed.  We will
21312f172c55SRobert Thurlow 		 * restore the whole servinfo4_t to what it was before.
21322f172c55SRobert Thurlow 		 */
21332f172c55SRobert Thurlow 		srvnext = svp->sv_next;
21342f172c55SRobert Thurlow 		svp->sv_next = NULL;
21352f172c55SRobert Thurlow 		tmpsrv = copy_svp(origsvp);
21362f172c55SRobert Thurlow 		sv4_free(svp);
21372f172c55SRobert Thurlow 		svp = tmpsrv;
21382f172c55SRobert Thurlow 		svp->sv_next = srvnext;
21392f172c55SRobert Thurlow 		mutex_enter(&mi->mi_lock);
21402f172c55SRobert Thurlow 		mi->mi_servers = svp;
21412f172c55SRobert Thurlow 		mi->mi_curr_serv = svp;
21422f172c55SRobert Thurlow 		mutex_exit(&mi->mi_lock);
21432f172c55SRobert Thurlow 
21442f172c55SRobert Thurlow 	} else if (origsvp->sv_pathlen != svp->sv_pathlen) {
21452f172c55SRobert Thurlow 
21462f172c55SRobert Thurlow 		/*
21472f172c55SRobert Thurlow 		 * For symlink case: restore original path because
21482f172c55SRobert Thurlow 		 * it might have contained symlinks that were
21492f172c55SRobert Thurlow 		 * expanded by nfsgetfh_otw before the failure occurred.
21502f172c55SRobert Thurlow 		 */
21515301ec54SRobert Thurlow 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
21522f172c55SRobert Thurlow 		kmem_free(svp->sv_path, svp->sv_pathlen);
21532f172c55SRobert Thurlow 		svp->sv_path =
21542f172c55SRobert Thurlow 		    kmem_alloc(origsvp->sv_pathlen, KM_SLEEP);
21552f172c55SRobert Thurlow 		svp->sv_pathlen = origsvp->sv_pathlen;
21562f172c55SRobert Thurlow 		bcopy(origsvp->sv_path, svp->sv_path,
21572f172c55SRobert Thurlow 		    origsvp->sv_pathlen);
21582f172c55SRobert Thurlow 		nfs_rw_exit(&svp->sv_lock);
21592f172c55SRobert Thurlow 	}
21602f172c55SRobert Thurlow 	return (svp);
21612f172c55SRobert Thurlow }
21622f172c55SRobert Thurlow 
21637c478bd9Sstevel@tonic-gate static ushort_t nfs4_max_threads = 8;	/* max number of active async threads */
2164c242f9a0Schunli zhang - Sun Microsystems - Irvine United States uint_t nfs4_bsize = 32 * 1024;	/* client `block' size */
21657c478bd9Sstevel@tonic-gate static uint_t nfs4_async_clusters = 1;	/* # of reqs from each async queue */
21667c478bd9Sstevel@tonic-gate static uint_t nfs4_cots_timeo = NFS_COTS_TIMEO;
21677c478bd9Sstevel@tonic-gate 
21687c478bd9Sstevel@tonic-gate /*
21697c478bd9Sstevel@tonic-gate  * Remap the root filehandle for the given filesystem.
21707c478bd9Sstevel@tonic-gate  *
21717c478bd9Sstevel@tonic-gate  * results returned via the nfs4_error_t parameter.
21727c478bd9Sstevel@tonic-gate  */
21737c478bd9Sstevel@tonic-gate void
nfs4_remap_root(mntinfo4_t * mi,nfs4_error_t * ep,int flags)21747c478bd9Sstevel@tonic-gate nfs4_remap_root(mntinfo4_t *mi, nfs4_error_t *ep, int flags)
21757c478bd9Sstevel@tonic-gate {
21762f172c55SRobert Thurlow 	struct servinfo4 *svp, *origsvp;
21777c478bd9Sstevel@tonic-gate 	vtype_t vtype;
21787c478bd9Sstevel@tonic-gate 	nfs_fh4 rootfh;
21797c478bd9Sstevel@tonic-gate 	int getfh_flags;
21802f172c55SRobert Thurlow 	int num_retry;
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
21838c9e5ad2Saalok 
21848c9e5ad2Saalok remap_retry:
21857c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
21867c478bd9Sstevel@tonic-gate 	getfh_flags =
21877c478bd9Sstevel@tonic-gate 	    (flags & NFS4_REMAP_NEEDSOP) ? NFS4_GETFH_NEEDSOP : 0;
21887c478bd9Sstevel@tonic-gate 	getfh_flags |=
21897c478bd9Sstevel@tonic-gate 	    (mi->mi_flags & MI4_PUBLIC) ? NFS4_GETFH_PUBLIC : 0;
21907c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate 	/*
21937c478bd9Sstevel@tonic-gate 	 * Just in case server path being mounted contains
21947c478bd9Sstevel@tonic-gate 	 * symlinks and fails w/STALE, save the initial sv_path
21957c478bd9Sstevel@tonic-gate 	 * so we can redrive the initial mount compound with the
21967c478bd9Sstevel@tonic-gate 	 * initial sv_path -- not a symlink-expanded version.
21977c478bd9Sstevel@tonic-gate 	 *
21987c478bd9Sstevel@tonic-gate 	 * This could only happen if a symlink was expanded
21997c478bd9Sstevel@tonic-gate 	 * and the expanded mount compound failed stale.  Because
22007c478bd9Sstevel@tonic-gate 	 * it could be the case that the symlink was removed at
22017c478bd9Sstevel@tonic-gate 	 * the server (and replaced with another symlink/dir,
22027c478bd9Sstevel@tonic-gate 	 * we need to use the initial sv_path when attempting
22037c478bd9Sstevel@tonic-gate 	 * to re-lookup everything and recover.
22047c478bd9Sstevel@tonic-gate 	 */
22057c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
22062f172c55SRobert Thurlow 	origsvp = copy_svp(svp);
22077c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
22087c478bd9Sstevel@tonic-gate 
22097c478bd9Sstevel@tonic-gate 	num_retry = nfs4_max_mount_retry;
22107c478bd9Sstevel@tonic-gate 
22117c478bd9Sstevel@tonic-gate 	do {
22127c478bd9Sstevel@tonic-gate 		/*
22137c478bd9Sstevel@tonic-gate 		 * Get the root fh from the server.  Retry nfs4_max_mount_retry
22147c478bd9Sstevel@tonic-gate 		 * (2) times if it fails with STALE since the recovery
22157c478bd9Sstevel@tonic-gate 		 * infrastructure doesn't do STALE recovery for components
22167c478bd9Sstevel@tonic-gate 		 * of the server path to the object being mounted.
22177c478bd9Sstevel@tonic-gate 		 */
22187c478bd9Sstevel@tonic-gate 		nfs4getfh_otw(mi, svp, &vtype, getfh_flags, CRED(), ep);
22197c478bd9Sstevel@tonic-gate 
22207c478bd9Sstevel@tonic-gate 		if (ep->error == 0 && ep->stat == NFS4_OK)
22217c478bd9Sstevel@tonic-gate 			break;
22227c478bd9Sstevel@tonic-gate 
22237c478bd9Sstevel@tonic-gate 		/*
22247c478bd9Sstevel@tonic-gate 		 * For some reason, the mount compound failed.  Before
22252f172c55SRobert Thurlow 		 * retrying, we need to restore original conditions.
22267c478bd9Sstevel@tonic-gate 		 */
22272f172c55SRobert Thurlow 		svp = restore_svp(mi, svp, origsvp);
22287c478bd9Sstevel@tonic-gate 
22297c478bd9Sstevel@tonic-gate 	} while (num_retry-- > 0);
22307c478bd9Sstevel@tonic-gate 
22312f172c55SRobert Thurlow 	sv4_free(origsvp);
22327c478bd9Sstevel@tonic-gate 
22337c478bd9Sstevel@tonic-gate 	if (ep->error != 0 || ep->stat != 0) {
22347c478bd9Sstevel@tonic-gate 		return;
22357c478bd9Sstevel@tonic-gate 	}
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate 	if (vtype != VNON && vtype != mi->mi_type) {
22387c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
22397c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_WARN,
22407c478bd9Sstevel@tonic-gate 		    "nfs4_remap_root: server root vnode type (%d) doesn't "
22417c478bd9Sstevel@tonic-gate 		    "match mount info (%d)", vtype, mi->mi_type);
22427c478bd9Sstevel@tonic-gate 	}
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
22457c478bd9Sstevel@tonic-gate 	rootfh.nfs_fh4_val = svp->sv_fhandle.fh_buf;
22467c478bd9Sstevel@tonic-gate 	rootfh.nfs_fh4_len = svp->sv_fhandle.fh_len;
22477c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
22487c478bd9Sstevel@tonic-gate 	sfh4_update(mi->mi_rootfh, &rootfh);
22497c478bd9Sstevel@tonic-gate 
22507c478bd9Sstevel@tonic-gate 	/*
22518c9e5ad2Saalok 	 * It's possible that recovery took place on the filesystem
22528c9e5ad2Saalok 	 * and the server has been updated between the time we did
22538c9e5ad2Saalok 	 * the nfs4getfh_otw and now. Re-drive the otw operation
22548c9e5ad2Saalok 	 * to make sure we have a good fh.
22557c478bd9Sstevel@tonic-gate 	 */
22567c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
22578c9e5ad2Saalok 	if (mi->mi_curr_serv != svp)
22588c9e5ad2Saalok 		goto remap_retry;
22598c9e5ad2Saalok 
22607c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
22617c478bd9Sstevel@tonic-gate }
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate static int
nfs4rootvp(vnode_t ** rtvpp,vfs_t * vfsp,struct servinfo4 * svp_head,int flags,cred_t * cr,zone_t * zone)22647c478bd9Sstevel@tonic-gate nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head,
22657c478bd9Sstevel@tonic-gate     int flags, cred_t *cr, zone_t *zone)
22667c478bd9Sstevel@tonic-gate {
22677c478bd9Sstevel@tonic-gate 	vnode_t *rtvp = NULL;
22687c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
22697c478bd9Sstevel@tonic-gate 	dev_t nfs_dev;
22707c478bd9Sstevel@tonic-gate 	int error = 0;
22717c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
22722f172c55SRobert Thurlow 	int i, len;
22737c478bd9Sstevel@tonic-gate 	struct vattr va;
22747c478bd9Sstevel@tonic-gate 	vtype_t vtype = VNON;
22757c478bd9Sstevel@tonic-gate 	vtype_t tmp_vtype = VNON;
22767c478bd9Sstevel@tonic-gate 	struct servinfo4 *firstsvp = NULL, *svp = svp_head;
22777c478bd9Sstevel@tonic-gate 	nfs4_oo_hash_bucket_t *bucketp;
22787c478bd9Sstevel@tonic-gate 	nfs_fh4 fh;
22797c478bd9Sstevel@tonic-gate 	char *droptext = "";
22807c478bd9Sstevel@tonic-gate 	struct nfs_stats *nfsstatsp;
22817c478bd9Sstevel@tonic-gate 	nfs4_fname_t *mfname;
22827c478bd9Sstevel@tonic-gate 	nfs4_error_t e;
22832f172c55SRobert Thurlow 	int num_retry, removed;
22847c478bd9Sstevel@tonic-gate 	cred_t *lcr = NULL, *tcr = cr;
22852f172c55SRobert Thurlow 	struct servinfo4 *origsvp;
22862f172c55SRobert Thurlow 	char *resource;
22877c478bd9Sstevel@tonic-gate 
2288108322fbScarlsonj 	nfsstatsp = zone_getspecific(nfsstat_zone_key, nfs_zone());
22897c478bd9Sstevel@tonic-gate 	ASSERT(nfsstatsp != NULL);
22907c478bd9Sstevel@tonic-gate 
2291108322fbScarlsonj 	ASSERT(nfs_zone() == zone);
22927c478bd9Sstevel@tonic-gate 	ASSERT(crgetref(cr));
22937c478bd9Sstevel@tonic-gate 
22947c478bd9Sstevel@tonic-gate 	/*
22957c478bd9Sstevel@tonic-gate 	 * Create a mount record and link it to the vfs struct.
22967c478bd9Sstevel@tonic-gate 	 */
22977c478bd9Sstevel@tonic-gate 	mi = kmem_zalloc(sizeof (*mi), KM_SLEEP);
22987c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_lock, NULL, MUTEX_DEFAULT, NULL);
22997c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_recovlock, NULL, RW_DEFAULT, NULL);
23007c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_rename_lock, NULL, RW_DEFAULT, NULL);
23017c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_fh_lock, NULL, RW_DEFAULT, NULL);
23027c478bd9Sstevel@tonic-gate 
23037c478bd9Sstevel@tonic-gate 	if (!(flags & NFSMNT_SOFT))
23047c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_HARD;
23057c478bd9Sstevel@tonic-gate 	if ((flags & NFSMNT_NOPRINT))
23067c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_NOPRINT;
23077c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_INT)
23087c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_INT;
23097c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_PUBLIC)
23107c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_PUBLIC;
2311b9238976Sth199096 	if (flags & NFSMNT_MIRRORMOUNT)
2312b9238976Sth199096 		mi->mi_flags |= MI4_MIRRORMOUNT;
23132f172c55SRobert Thurlow 	if (flags & NFSMNT_REFERRAL)
23142f172c55SRobert Thurlow 		mi->mi_flags |= MI4_REFERRAL;
23157c478bd9Sstevel@tonic-gate 	mi->mi_retrans = NFS_RETRIES;
23167c478bd9Sstevel@tonic-gate 	if (svp->sv_knconf->knc_semantics == NC_TPI_COTS_ORD ||
23177c478bd9Sstevel@tonic-gate 	    svp->sv_knconf->knc_semantics == NC_TPI_COTS)
23187c478bd9Sstevel@tonic-gate 		mi->mi_timeo = nfs4_cots_timeo;
23197c478bd9Sstevel@tonic-gate 	else
23207c478bd9Sstevel@tonic-gate 		mi->mi_timeo = NFS_TIMEO;
23217c478bd9Sstevel@tonic-gate 	mi->mi_prog = NFS_PROGRAM;
23227c478bd9Sstevel@tonic-gate 	mi->mi_vers = NFS_V4;
23237c478bd9Sstevel@tonic-gate 	mi->mi_rfsnames = rfsnames_v4;
23247c478bd9Sstevel@tonic-gate 	mi->mi_reqs = nfsstatsp->nfs_stats_v4.rfsreqcnt_ptr;
23257c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_failover_cv, NULL, CV_DEFAULT, NULL);
23267c478bd9Sstevel@tonic-gate 	mi->mi_servers = svp;
23277c478bd9Sstevel@tonic-gate 	mi->mi_curr_serv = svp;
23287c478bd9Sstevel@tonic-gate 	mi->mi_acregmin = SEC2HR(ACREGMIN);
23297c478bd9Sstevel@tonic-gate 	mi->mi_acregmax = SEC2HR(ACREGMAX);
23307c478bd9Sstevel@tonic-gate 	mi->mi_acdirmin = SEC2HR(ACDIRMIN);
23317c478bd9Sstevel@tonic-gate 	mi->mi_acdirmax = SEC2HR(ACDIRMAX);
23327c478bd9Sstevel@tonic-gate 	mi->mi_fh_expire_type = FH4_PERSISTENT;
23337c478bd9Sstevel@tonic-gate 	mi->mi_clientid_next = NULL;
23347c478bd9Sstevel@tonic-gate 	mi->mi_clientid_prev = NULL;
23353b895386SPavel Filipensky 	mi->mi_srv = NULL;
23367c478bd9Sstevel@tonic-gate 	mi->mi_grace_wait = 0;
23377c478bd9Sstevel@tonic-gate 	mi->mi_error = 0;
23387c478bd9Sstevel@tonic-gate 	mi->mi_srvsettime = 0;
23393b895386SPavel Filipensky 	mi->mi_srvset_cnt = 0;
23407c478bd9Sstevel@tonic-gate 
234150a83466Sjwahlig 	mi->mi_count = 1;
234250a83466Sjwahlig 
23437c478bd9Sstevel@tonic-gate 	mi->mi_tsize = nfs4_tsize(svp->sv_knconf);
23447c478bd9Sstevel@tonic-gate 	mi->mi_stsize = mi->mi_tsize;
23457c478bd9Sstevel@tonic-gate 
23467c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_DIRECTIO)
23477c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_DIRECTIO;
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate 	mi->mi_flags |= MI4_MOUNTING;
23507c478bd9Sstevel@tonic-gate 
2351*92034044SMarcel Telka 	mutex_init(&mi->mi_rnodes_lock, NULL, MUTEX_DEFAULT, NULL);
2352*92034044SMarcel Telka 	list_create(&mi->mi_rnodes, sizeof (rnode4_t),
2353*92034044SMarcel Telka 	    offsetof(rnode4_t, r_mi_link));
2354*92034044SMarcel Telka 
23557c478bd9Sstevel@tonic-gate 	/*
23567c478bd9Sstevel@tonic-gate 	 * Make a vfs struct for nfs.  We do this here instead of below
23577c478bd9Sstevel@tonic-gate 	 * because rtvp needs a vfs before we can do a getattr on it.
23587c478bd9Sstevel@tonic-gate 	 *
23597c478bd9Sstevel@tonic-gate 	 * Assign a unique device id to the mount
23607c478bd9Sstevel@tonic-gate 	 */
23617c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs_minor_lock);
23627c478bd9Sstevel@tonic-gate 	do {
23637c478bd9Sstevel@tonic-gate 		nfs_minor = (nfs_minor + 1) & MAXMIN32;
23647c478bd9Sstevel@tonic-gate 		nfs_dev = makedevice(nfs_major, nfs_minor);
23657c478bd9Sstevel@tonic-gate 	} while (vfs_devismounted(nfs_dev));
23667c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs_minor_lock);
23677c478bd9Sstevel@tonic-gate 
23687c478bd9Sstevel@tonic-gate 	vfsp->vfs_dev = nfs_dev;
23697c478bd9Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, nfs_dev, nfs4fstyp);
23707c478bd9Sstevel@tonic-gate 	vfsp->vfs_data = (caddr_t)mi;
23717c478bd9Sstevel@tonic-gate 	vfsp->vfs_fstype = nfsfstyp;
23727c478bd9Sstevel@tonic-gate 	vfsp->vfs_bsize = nfs4_bsize;
23737c478bd9Sstevel@tonic-gate 
23747c478bd9Sstevel@tonic-gate 	/*
23757c478bd9Sstevel@tonic-gate 	 * Initialize fields used to support async putpage operations.
23767c478bd9Sstevel@tonic-gate 	 */
23777c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFS4_ASYNC_TYPES; i++)
23787c478bd9Sstevel@tonic-gate 		mi->mi_async_clusters[i] = nfs4_async_clusters;
23797c478bd9Sstevel@tonic-gate 	mi->mi_async_init_clusters = nfs4_async_clusters;
23800776f5e6SVallish Vaidyeshwara 	mi->mi_async_curr[NFS4_ASYNC_QUEUE] =
23810776f5e6SVallish Vaidyeshwara 	    mi->mi_async_curr[NFS4_ASYNC_PGOPS_QUEUE] = &mi->mi_async_reqs[0];
23827c478bd9Sstevel@tonic-gate 	mi->mi_max_threads = nfs4_max_threads;
23837c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_async_lock, NULL, MUTEX_DEFAULT, NULL);
23847c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_reqs_cv, NULL, CV_DEFAULT, NULL);
23850776f5e6SVallish Vaidyeshwara 	cv_init(&mi->mi_async_work_cv[NFS4_ASYNC_QUEUE], NULL, CV_DEFAULT,
23860776f5e6SVallish Vaidyeshwara 	    NULL);
23870776f5e6SVallish Vaidyeshwara 	cv_init(&mi->mi_async_work_cv[NFS4_ASYNC_PGOPS_QUEUE], NULL,
23880776f5e6SVallish Vaidyeshwara 	    CV_DEFAULT, NULL);
23897c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_cv, NULL, CV_DEFAULT, NULL);
23907c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_inact_req_cv, NULL, CV_DEFAULT, NULL);
23917c478bd9Sstevel@tonic-gate 
23927c478bd9Sstevel@tonic-gate 	mi->mi_vfsp = vfsp;
2393a19609f8Sjv227347 	mi->mi_zone = zone;
2394a19609f8Sjv227347 	zone_init_ref(&mi->mi_zone_ref);
2395a19609f8Sjv227347 	zone_hold_ref(zone, &mi->mi_zone_ref, ZONE_REF_NFSV4);
23967c478bd9Sstevel@tonic-gate 	nfs4_mi_zonelist_add(mi);
23977c478bd9Sstevel@tonic-gate 
23987c478bd9Sstevel@tonic-gate 	/*
23997c478bd9Sstevel@tonic-gate 	 * Initialize the <open owner/cred> hash table.
24007c478bd9Sstevel@tonic-gate 	 */
24017c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFS4_NUM_OO_BUCKETS; i++) {
24027c478bd9Sstevel@tonic-gate 		bucketp = &(mi->mi_oo_list[i]);
24037c478bd9Sstevel@tonic-gate 		mutex_init(&bucketp->b_lock, NULL, MUTEX_DEFAULT, NULL);
24047c478bd9Sstevel@tonic-gate 		list_create(&bucketp->b_oo_hash_list,
24057c478bd9Sstevel@tonic-gate 		    sizeof (nfs4_open_owner_t),
24067c478bd9Sstevel@tonic-gate 		    offsetof(nfs4_open_owner_t, oo_hash_node));
24077c478bd9Sstevel@tonic-gate 	}
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate 	/*
24107c478bd9Sstevel@tonic-gate 	 * Initialize the freed open owner list.
24117c478bd9Sstevel@tonic-gate 	 */
24127c478bd9Sstevel@tonic-gate 	mi->mi_foo_num = 0;
24137c478bd9Sstevel@tonic-gate 	mi->mi_foo_max = NFS4_NUM_FREED_OPEN_OWNERS;
24147c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_foo_list, sizeof (nfs4_open_owner_t),
24157c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_open_owner_t, oo_foo_node));
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_lost_state, sizeof (nfs4_lost_rqst_t),
24187c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_lost_rqst_t, lr_node));
24197c478bd9Sstevel@tonic-gate 
24207c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_bseqid_list, sizeof (nfs4_bseqid_entry_t),
24217c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_bseqid_entry_t, bs_node));
24227c478bd9Sstevel@tonic-gate 
24237c478bd9Sstevel@tonic-gate 	/*
24247c478bd9Sstevel@tonic-gate 	 * Initialize the msg buffer.
24257c478bd9Sstevel@tonic-gate 	 */
24267c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_msg_list, sizeof (nfs4_debug_msg_t),
24277c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_debug_msg_t, msg_node));
24287c478bd9Sstevel@tonic-gate 	mi->mi_msg_count = 0;
24297c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_msg_list_lock, NULL, MUTEX_DEFAULT, NULL);
24307c478bd9Sstevel@tonic-gate 
24317c478bd9Sstevel@tonic-gate 	/*
24327c478bd9Sstevel@tonic-gate 	 * Initialize kstats
24337c478bd9Sstevel@tonic-gate 	 */
24347c478bd9Sstevel@tonic-gate 	nfs4_mnt_kstat_init(vfsp);
24357c478bd9Sstevel@tonic-gate 
24367c478bd9Sstevel@tonic-gate 	/*
2437bbf2a467SNagakiran Rajashekar 	 * Initialize the shared filehandle pool.
24387c478bd9Sstevel@tonic-gate 	 */
24397c478bd9Sstevel@tonic-gate 	sfh4_createtab(&mi->mi_filehandles);
24407c478bd9Sstevel@tonic-gate 
24417c478bd9Sstevel@tonic-gate 	/*
24427c478bd9Sstevel@tonic-gate 	 * Save server path we're attempting to mount.
24437c478bd9Sstevel@tonic-gate 	 */
24447c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
24452f172c55SRobert Thurlow 	origsvp = copy_svp(svp);
24467c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
24477c478bd9Sstevel@tonic-gate 
24487c478bd9Sstevel@tonic-gate 	/*
24497c478bd9Sstevel@tonic-gate 	 * Make the GETFH call to get root fh for each replica.
24507c478bd9Sstevel@tonic-gate 	 */
24517c478bd9Sstevel@tonic-gate 	if (svp_head->sv_next)
24527c478bd9Sstevel@tonic-gate 		droptext = ", dropping replica";
24537c478bd9Sstevel@tonic-gate 
24547c478bd9Sstevel@tonic-gate 	/*
24557c478bd9Sstevel@tonic-gate 	 * If the uid is set then set the creds for secure mounts
24567c478bd9Sstevel@tonic-gate 	 * by proxy processes such as automountd.
24577c478bd9Sstevel@tonic-gate 	 */
24587c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
2459f722863dSSiyamaladevi Santhana Krishnan 	if (svp->sv_secdata->uid != 0 &&
2460f722863dSSiyamaladevi Santhana Krishnan 	    svp->sv_secdata->rpcflavor == RPCSEC_GSS) {
24617c478bd9Sstevel@tonic-gate 		lcr = crdup(cr);
24627c478bd9Sstevel@tonic-gate 		(void) crsetugid(lcr, svp->sv_secdata->uid, crgetgid(cr));
24637c478bd9Sstevel@tonic-gate 		tcr = lcr;
24647c478bd9Sstevel@tonic-gate 	}
24657c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
24667c478bd9Sstevel@tonic-gate 	for (svp = svp_head; svp; svp = svp->sv_next) {
24677c478bd9Sstevel@tonic-gate 		if (nfs4_chkdup_servinfo4(svp_head, svp)) {
24687c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
24697c478bd9Sstevel@tonic-gate 			    VERS_MSG "Host %s is a duplicate%s",
24707c478bd9Sstevel@tonic-gate 			    svp->sv_hostname, droptext);
24717c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
24727c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
24737c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
24747c478bd9Sstevel@tonic-gate 			continue;
24757c478bd9Sstevel@tonic-gate 		}
24767c478bd9Sstevel@tonic-gate 		mi->mi_curr_serv = svp;
24777c478bd9Sstevel@tonic-gate 
24787c478bd9Sstevel@tonic-gate 		/*
24797c478bd9Sstevel@tonic-gate 		 * Just in case server path being mounted contains
24807c478bd9Sstevel@tonic-gate 		 * symlinks and fails w/STALE, save the initial sv_path
24817c478bd9Sstevel@tonic-gate 		 * so we can redrive the initial mount compound with the
24827c478bd9Sstevel@tonic-gate 		 * initial sv_path -- not a symlink-expanded version.
24837c478bd9Sstevel@tonic-gate 		 *
24847c478bd9Sstevel@tonic-gate 		 * This could only happen if a symlink was expanded
24857c478bd9Sstevel@tonic-gate 		 * and the expanded mount compound failed stale.  Because
24867c478bd9Sstevel@tonic-gate 		 * it could be the case that the symlink was removed at
24877c478bd9Sstevel@tonic-gate 		 * the server (and replaced with another symlink/dir,
24887c478bd9Sstevel@tonic-gate 		 * we need to use the initial sv_path when attempting
24897c478bd9Sstevel@tonic-gate 		 * to re-lookup everything and recover.
24907c478bd9Sstevel@tonic-gate 		 *
24917c478bd9Sstevel@tonic-gate 		 * Other mount errors should evenutally be handled here also
24927c478bd9Sstevel@tonic-gate 		 * (NFS4ERR_DELAY, NFS4ERR_RESOURCE).  For now, all mount
24937c478bd9Sstevel@tonic-gate 		 * failures will result in mount being redriven a few times.
24947c478bd9Sstevel@tonic-gate 		 */
24957c478bd9Sstevel@tonic-gate 		num_retry = nfs4_max_mount_retry;
24967c478bd9Sstevel@tonic-gate 		do {
24977c478bd9Sstevel@tonic-gate 			nfs4getfh_otw(mi, svp, &tmp_vtype,
24987c478bd9Sstevel@tonic-gate 			    ((flags & NFSMNT_PUBLIC) ? NFS4_GETFH_PUBLIC : 0) |
24997c478bd9Sstevel@tonic-gate 			    NFS4_GETFH_NEEDSOP, tcr, &e);
25007c478bd9Sstevel@tonic-gate 
25017c478bd9Sstevel@tonic-gate 			if (e.error == 0 && e.stat == NFS4_OK)
25027c478bd9Sstevel@tonic-gate 				break;
25037c478bd9Sstevel@tonic-gate 
25047c478bd9Sstevel@tonic-gate 			/*
25052f172c55SRobert Thurlow 			 * For some reason, the mount compound failed.  Before
25062f172c55SRobert Thurlow 			 * retrying, we need to restore original conditions.
25077c478bd9Sstevel@tonic-gate 			 */
25082f172c55SRobert Thurlow 			svp = restore_svp(mi, svp, origsvp);
25092f172c55SRobert Thurlow 			svp_head = svp;
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 		} while (num_retry-- > 0);
25127c478bd9Sstevel@tonic-gate 		error = e.error ? e.error : geterrno4(e.stat);
25137c478bd9Sstevel@tonic-gate 		if (error) {
25147c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
25157c478bd9Sstevel@tonic-gate 			    VERS_MSG "initial call to %s failed%s: %m",
25167c478bd9Sstevel@tonic-gate 			    svp->sv_hostname, droptext);
25177c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25187c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25197c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25207c478bd9Sstevel@tonic-gate 			mi->mi_flags &= ~MI4_RECOV_FAIL;
25217c478bd9Sstevel@tonic-gate 			mi->mi_error = 0;
25227c478bd9Sstevel@tonic-gate 			continue;
25237c478bd9Sstevel@tonic-gate 		}
25247c478bd9Sstevel@tonic-gate 
25257c478bd9Sstevel@tonic-gate 		if (tmp_vtype == VBAD) {
25267c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_WARN,
25277c478bd9Sstevel@tonic-gate 			    VERS_MSG "%s returned a bad file type for "
25287c478bd9Sstevel@tonic-gate 			    "root%s", svp->sv_hostname, droptext);
25297c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25307c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25317c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25327c478bd9Sstevel@tonic-gate 			continue;
25337c478bd9Sstevel@tonic-gate 		}
25347c478bd9Sstevel@tonic-gate 
25357c478bd9Sstevel@tonic-gate 		if (vtype == VNON) {
25367c478bd9Sstevel@tonic-gate 			vtype = tmp_vtype;
25377c478bd9Sstevel@tonic-gate 		} else if (vtype != tmp_vtype) {
25387c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_WARN,
25397c478bd9Sstevel@tonic-gate 			    VERS_MSG "%s returned a different file type "
25407c478bd9Sstevel@tonic-gate 			    "for root%s", svp->sv_hostname, droptext);
25417c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25427c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25437c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25447c478bd9Sstevel@tonic-gate 			continue;
25457c478bd9Sstevel@tonic-gate 		}
25467c478bd9Sstevel@tonic-gate 		if (firstsvp == NULL)
25477c478bd9Sstevel@tonic-gate 			firstsvp = svp;
25487c478bd9Sstevel@tonic-gate 	}
25497c478bd9Sstevel@tonic-gate 
25507c478bd9Sstevel@tonic-gate 	if (firstsvp == NULL) {
25517c478bd9Sstevel@tonic-gate 		if (error == 0)
25527c478bd9Sstevel@tonic-gate 			error = ENOENT;
25537c478bd9Sstevel@tonic-gate 		goto bad;
25547c478bd9Sstevel@tonic-gate 	}
25557c478bd9Sstevel@tonic-gate 
25567c478bd9Sstevel@tonic-gate 	mi->mi_curr_serv = svp = firstsvp;
25577c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
25587c478bd9Sstevel@tonic-gate 	ASSERT((mi->mi_curr_serv->sv_flags & SV4_NOTINUSE) == 0);
25597c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_len = svp->sv_fhandle.fh_len;
25607c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_val = svp->sv_fhandle.fh_buf;
25617c478bd9Sstevel@tonic-gate 	mi->mi_rootfh = sfh4_get(&fh, mi);
25627c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_len = svp->sv_pfhandle.fh_len;
25637c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_val = svp->sv_pfhandle.fh_buf;
25647c478bd9Sstevel@tonic-gate 	mi->mi_srvparentfh = sfh4_get(&fh, mi);
25657c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
25667c478bd9Sstevel@tonic-gate 
25677c478bd9Sstevel@tonic-gate 	/*
2568bbf2a467SNagakiran Rajashekar 	 * Get the fname for filesystem root.
25697c478bd9Sstevel@tonic-gate 	 */
2570bbf2a467SNagakiran Rajashekar 	mi->mi_fname = fn_get(NULL, ".", mi->mi_rootfh);
25717c478bd9Sstevel@tonic-gate 	mfname = mi->mi_fname;
25727c478bd9Sstevel@tonic-gate 	fn_hold(mfname);
2573bbf2a467SNagakiran Rajashekar 
2574bbf2a467SNagakiran Rajashekar 	/*
2575bbf2a467SNagakiran Rajashekar 	 * Make the root vnode without attributes.
2576bbf2a467SNagakiran Rajashekar 	 */
25777c478bd9Sstevel@tonic-gate 	rtvp = makenfs4node_by_fh(mi->mi_rootfh, NULL,
25787c478bd9Sstevel@tonic-gate 	    &mfname, NULL, mi, cr, gethrtime());
25797c478bd9Sstevel@tonic-gate 	rtvp->v_type = vtype;
25807c478bd9Sstevel@tonic-gate 
25817c478bd9Sstevel@tonic-gate 	mi->mi_curread = mi->mi_tsize;
25827c478bd9Sstevel@tonic-gate 	mi->mi_curwrite = mi->mi_stsize;
25837c478bd9Sstevel@tonic-gate 
25847c478bd9Sstevel@tonic-gate 	/*
25857c478bd9Sstevel@tonic-gate 	 * Start the manager thread responsible for handling async worker
25867c478bd9Sstevel@tonic-gate 	 * threads.
25877c478bd9Sstevel@tonic-gate 	 */
258850a83466Sjwahlig 	MI4_HOLD(mi);
25897c478bd9Sstevel@tonic-gate 	VFS_HOLD(vfsp);	/* add reference for thread */
25907c478bd9Sstevel@tonic-gate 	mi->mi_manager_thread = zthread_create(NULL, 0, nfs4_async_manager,
25917c478bd9Sstevel@tonic-gate 	    vfsp, 0, minclsyspri);
25927c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_manager_thread != NULL);
259350a83466Sjwahlig 
25947c478bd9Sstevel@tonic-gate 	/*
25957c478bd9Sstevel@tonic-gate 	 * Create the thread that handles over-the-wire calls for
25967c478bd9Sstevel@tonic-gate 	 * VOP_INACTIVE.
25977c478bd9Sstevel@tonic-gate 	 * This needs to happen after the manager thread is created.
25987c478bd9Sstevel@tonic-gate 	 */
259950a83466Sjwahlig 	MI4_HOLD(mi);
26007c478bd9Sstevel@tonic-gate 	mi->mi_inactive_thread = zthread_create(NULL, 0, nfs4_inactive_thread,
26017c478bd9Sstevel@tonic-gate 	    mi, 0, minclsyspri);
26027c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_inactive_thread != NULL);
26037c478bd9Sstevel@tonic-gate 
26047c478bd9Sstevel@tonic-gate 	/* If we didn't get a type, get one now */
26057c478bd9Sstevel@tonic-gate 	if (rtvp->v_type == VNON) {
26067c478bd9Sstevel@tonic-gate 		va.va_mask = AT_TYPE;
26077c478bd9Sstevel@tonic-gate 		error = nfs4getattr(rtvp, &va, tcr);
26087c478bd9Sstevel@tonic-gate 		if (error)
26097c478bd9Sstevel@tonic-gate 			goto bad;
26107c478bd9Sstevel@tonic-gate 		rtvp->v_type = va.va_type;
26117c478bd9Sstevel@tonic-gate 	}
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 	mi->mi_type = rtvp->v_type;
26147c478bd9Sstevel@tonic-gate 
26157c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
26167c478bd9Sstevel@tonic-gate 	mi->mi_flags &= ~MI4_MOUNTING;
26177c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
26187c478bd9Sstevel@tonic-gate 
26192f172c55SRobert Thurlow 	/* Update VFS with new server and path info */
26202f172c55SRobert Thurlow 	if ((strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) ||
26212f172c55SRobert Thurlow 	    (strcmp(svp->sv_path, origsvp->sv_path) != 0)) {
26222f172c55SRobert Thurlow 		len = svp->sv_hostnamelen + svp->sv_pathlen;
26232f172c55SRobert Thurlow 		resource = kmem_zalloc(len, KM_SLEEP);
26242f172c55SRobert Thurlow 		(void) strcat(resource, svp->sv_hostname);
26252f172c55SRobert Thurlow 		(void) strcat(resource, ":");
26262f172c55SRobert Thurlow 		(void) strcat(resource, svp->sv_path);
2627d7de0ceaSRobert Harris 		vfs_setresource(vfsp, resource, 0);
26282f172c55SRobert Thurlow 		kmem_free(resource, len);
26292f172c55SRobert Thurlow 	}
26302f172c55SRobert Thurlow 
26312f172c55SRobert Thurlow 	sv4_free(origsvp);
26327c478bd9Sstevel@tonic-gate 	*rtvpp = rtvp;
26337c478bd9Sstevel@tonic-gate 	if (lcr != NULL)
26347c478bd9Sstevel@tonic-gate 		crfree(lcr);
26357c478bd9Sstevel@tonic-gate 
26367c478bd9Sstevel@tonic-gate 	return (0);
26377c478bd9Sstevel@tonic-gate bad:
26387c478bd9Sstevel@tonic-gate 	/*
26397c478bd9Sstevel@tonic-gate 	 * An error occurred somewhere, need to clean up...
26407c478bd9Sstevel@tonic-gate 	 */
26417c478bd9Sstevel@tonic-gate 	if (lcr != NULL)
26427c478bd9Sstevel@tonic-gate 		crfree(lcr);
2643b9238976Sth199096 
26447c478bd9Sstevel@tonic-gate 	if (rtvp != NULL) {
26457c478bd9Sstevel@tonic-gate 		/*
26467c478bd9Sstevel@tonic-gate 		 * We need to release our reference to the root vnode and
26477c478bd9Sstevel@tonic-gate 		 * destroy the mntinfo4 struct that we just created.
26487c478bd9Sstevel@tonic-gate 		 */
26497c478bd9Sstevel@tonic-gate 		rp = VTOR4(rtvp);
26507c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R4HASHED)
26517c478bd9Sstevel@tonic-gate 			rp4_rmhash(rp);
26527c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
26537c478bd9Sstevel@tonic-gate 	}
26547c478bd9Sstevel@tonic-gate 	nfs4_async_stop(vfsp);
26557c478bd9Sstevel@tonic-gate 	nfs4_async_manager_stop(vfsp);
265650a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
265750a83466Sjwahlig 	if (removed)
2658a19609f8Sjv227347 		zone_rele_ref(&mi->mi_zone_ref, ZONE_REF_NFSV4);
265950a83466Sjwahlig 
266050a83466Sjwahlig 	/*
266150a83466Sjwahlig 	 * This releases the initial "hold" of the mi since it will never
266250a83466Sjwahlig 	 * be referenced by the vfsp.  Also, when mount returns to vfs.c
266350a83466Sjwahlig 	 * with an error, the vfsp will be destroyed, not rele'd.
266450a83466Sjwahlig 	 */
266550a83466Sjwahlig 	MI4_RELE(mi);
266650a83466Sjwahlig 
26672f172c55SRobert Thurlow 	if (origsvp != NULL)
26682f172c55SRobert Thurlow 		sv4_free(origsvp);
26692f172c55SRobert Thurlow 
26707c478bd9Sstevel@tonic-gate 	*rtvpp = NULL;
26717c478bd9Sstevel@tonic-gate 	return (error);
26727c478bd9Sstevel@tonic-gate }
26737c478bd9Sstevel@tonic-gate 
26747c478bd9Sstevel@tonic-gate /*
26757c478bd9Sstevel@tonic-gate  * vfs operations
26767c478bd9Sstevel@tonic-gate  */
26777c478bd9Sstevel@tonic-gate static int
nfs4_unmount(vfs_t * vfsp,int flag,cred_t * cr)26787c478bd9Sstevel@tonic-gate nfs4_unmount(vfs_t *vfsp, int flag, cred_t *cr)
26797c478bd9Sstevel@tonic-gate {
26807c478bd9Sstevel@tonic-gate 	mntinfo4_t		*mi;
26817c478bd9Sstevel@tonic-gate 	ushort_t		omax;
268250a83466Sjwahlig 	int			removed;
26837c478bd9Sstevel@tonic-gate 
2684d3a14591SThomas Haynes 	bool_t			must_unlock;
2685b9238976Sth199096 
2686b9238976Sth199096 	nfs4_ephemeral_tree_t	*eph_tree;
2687b9238976Sth199096 
26887c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
26897c478bd9Sstevel@tonic-gate 		return (EPERM);
26907c478bd9Sstevel@tonic-gate 
26917c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
26927c478bd9Sstevel@tonic-gate 
26937c478bd9Sstevel@tonic-gate 	if (flag & MS_FORCE) {
26947c478bd9Sstevel@tonic-gate 		vfsp->vfs_flag |= VFS_UNMOUNTED;
2695108322fbScarlsonj 		if (nfs_zone() != mi->mi_zone) {
26967c478bd9Sstevel@tonic-gate 			/*
26977c478bd9Sstevel@tonic-gate 			 * If the request is coming from the wrong zone,
26987c478bd9Sstevel@tonic-gate 			 * we don't want to create any new threads, and
26997c478bd9Sstevel@tonic-gate 			 * performance is not a concern.  Do everything
27007c478bd9Sstevel@tonic-gate 			 * inline.
27017c478bd9Sstevel@tonic-gate 			 */
27027c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_zone_debug, (CE_NOTE,
27037c478bd9Sstevel@tonic-gate 			    "nfs4_unmount x-zone forced unmount of vfs %p\n",
27047c478bd9Sstevel@tonic-gate 			    (void *)vfsp));
2705b9238976Sth199096 			nfs4_free_mount(vfsp, flag, cr);
27067c478bd9Sstevel@tonic-gate 		} else {
27077c478bd9Sstevel@tonic-gate 			/*
27087c478bd9Sstevel@tonic-gate 			 * Free data structures asynchronously, to avoid
27097c478bd9Sstevel@tonic-gate 			 * blocking the current thread (for performance
27107c478bd9Sstevel@tonic-gate 			 * reasons only).
27117c478bd9Sstevel@tonic-gate 			 */
2712b9238976Sth199096 			async_free_mount(vfsp, flag, cr);
27137c478bd9Sstevel@tonic-gate 		}
2714b9238976Sth199096 
27157c478bd9Sstevel@tonic-gate 		return (0);
27167c478bd9Sstevel@tonic-gate 	}
2717b9238976Sth199096 
27187c478bd9Sstevel@tonic-gate 	/*
27197c478bd9Sstevel@tonic-gate 	 * Wait until all asynchronous putpage operations on
27207c478bd9Sstevel@tonic-gate 	 * this file system are complete before flushing rnodes
27217c478bd9Sstevel@tonic-gate 	 * from the cache.
27227c478bd9Sstevel@tonic-gate 	 */
27237c478bd9Sstevel@tonic-gate 	omax = mi->mi_max_threads;
2724b9238976Sth199096 	if (nfs4_async_stop_sig(vfsp))
27257c478bd9Sstevel@tonic-gate 		return (EINTR);
2726b9238976Sth199096 
27277c478bd9Sstevel@tonic-gate 	r4flush(vfsp, cr);
2728b9238976Sth199096 
2729eabd0450Sth199096 	/*
2730eabd0450Sth199096 	 * About the only reason that this would fail would be
2731eabd0450Sth199096 	 * that the harvester is already busy tearing down this
2732eabd0450Sth199096 	 * node. So we fail back to the caller and let them try
2733eabd0450Sth199096 	 * again when needed.
2734eabd0450Sth199096 	 */
2735eabd0450Sth199096 	if (nfs4_ephemeral_umount(mi, flag, cr,
27362f172c55SRobert Thurlow 	    &must_unlock, &eph_tree)) {
2737d3a14591SThomas Haynes 		ASSERT(must_unlock == FALSE);
2738eabd0450Sth199096 		mutex_enter(&mi->mi_async_lock);
2739eabd0450Sth199096 		mi->mi_max_threads = omax;
2740eabd0450Sth199096 		mutex_exit(&mi->mi_async_lock);
2741eabd0450Sth199096 
2742eabd0450Sth199096 		return (EBUSY);
2743eabd0450Sth199096 	}
2744b9238976Sth199096 
27457c478bd9Sstevel@tonic-gate 	/*
27467c478bd9Sstevel@tonic-gate 	 * If there are any active vnodes on this file system,
2747b9238976Sth199096 	 * then the file system is busy and can't be unmounted.
27487c478bd9Sstevel@tonic-gate 	 */
27497c478bd9Sstevel@tonic-gate 	if (check_rtable4(vfsp)) {
27502f172c55SRobert Thurlow 		nfs4_ephemeral_umount_unlock(&must_unlock, &eph_tree);
2751b9238976Sth199096 
27527c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_async_lock);
27537c478bd9Sstevel@tonic-gate 		mi->mi_max_threads = omax;
27547c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_async_lock);
2755b9238976Sth199096 
27567c478bd9Sstevel@tonic-gate 		return (EBUSY);
27577c478bd9Sstevel@tonic-gate 	}
2758b9238976Sth199096 
27597c478bd9Sstevel@tonic-gate 	/*
2760b9238976Sth199096 	 * The unmount can't fail from now on, so record any
2761b9238976Sth199096 	 * ephemeral changes.
2762b9238976Sth199096 	 */
27632f172c55SRobert Thurlow 	nfs4_ephemeral_umount_activate(mi, &must_unlock, &eph_tree);
2764b9238976Sth199096 
2765b9238976Sth199096 	/*
2766b9238976Sth199096 	 * There are no active files that could require over-the-wire
2767b9238976Sth199096 	 * calls to the server, so stop the async manager and the
2768b9238976Sth199096 	 * inactive thread.
27697c478bd9Sstevel@tonic-gate 	 */
27707c478bd9Sstevel@tonic-gate 	nfs4_async_manager_stop(vfsp);
2771b9238976Sth199096 
27727c478bd9Sstevel@tonic-gate 	/*
27737c478bd9Sstevel@tonic-gate 	 * Destroy all rnodes belonging to this file system from the
27747c478bd9Sstevel@tonic-gate 	 * rnode hash queues and purge any resources allocated to
27757c478bd9Sstevel@tonic-gate 	 * them.
27767c478bd9Sstevel@tonic-gate 	 */
27777c478bd9Sstevel@tonic-gate 	destroy_rtable4(vfsp, cr);
27787c478bd9Sstevel@tonic-gate 	vfsp->vfs_flag |= VFS_UNMOUNTED;
277950a83466Sjwahlig 
27807c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server(mi, NULL);
278150a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
278250a83466Sjwahlig 	if (removed)
2783a19609f8Sjv227347 		zone_rele_ref(&mi->mi_zone_ref, ZONE_REF_NFSV4);
278450a83466Sjwahlig 
27857c478bd9Sstevel@tonic-gate 	return (0);
27867c478bd9Sstevel@tonic-gate }
27877c478bd9Sstevel@tonic-gate 
27887c478bd9Sstevel@tonic-gate /*
27897c478bd9Sstevel@tonic-gate  * find root of nfs
27907c478bd9Sstevel@tonic-gate  */
27917c478bd9Sstevel@tonic-gate static int
nfs4_root(vfs_t * vfsp,vnode_t ** vpp)27927c478bd9Sstevel@tonic-gate nfs4_root(vfs_t *vfsp, vnode_t **vpp)
27937c478bd9Sstevel@tonic-gate {
27947c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
27957c478bd9Sstevel@tonic-gate 	vnode_t *vp;
27967c478bd9Sstevel@tonic-gate 	nfs4_fname_t *mfname;
27977c478bd9Sstevel@tonic-gate 	servinfo4_t *svp;
27987c478bd9Sstevel@tonic-gate 
27997c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
28007c478bd9Sstevel@tonic-gate 
2801108322fbScarlsonj 	if (nfs_zone() != mi->mi_zone)
28027c478bd9Sstevel@tonic-gate 		return (EPERM);
28037c478bd9Sstevel@tonic-gate 
28047c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
28057c478bd9Sstevel@tonic-gate 	if (svp) {
28067c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
28077c478bd9Sstevel@tonic-gate 		if (svp->sv_flags & SV4_ROOT_STALE) {
28087c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
28117c478bd9Sstevel@tonic-gate 			if (svp->sv_flags & SV4_ROOT_STALE) {
28127c478bd9Sstevel@tonic-gate 				svp->sv_flags &= ~SV4_ROOT_STALE;
28137c478bd9Sstevel@tonic-gate 				nfs_rw_exit(&svp->sv_lock);
28147c478bd9Sstevel@tonic-gate 				return (ENOENT);
28157c478bd9Sstevel@tonic-gate 			}
28167c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28177c478bd9Sstevel@tonic-gate 		} else
28187c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28197c478bd9Sstevel@tonic-gate 	}
28207c478bd9Sstevel@tonic-gate 
28217c478bd9Sstevel@tonic-gate 	mfname = mi->mi_fname;
28227c478bd9Sstevel@tonic-gate 	fn_hold(mfname);
28237c478bd9Sstevel@tonic-gate 	vp = makenfs4node_by_fh(mi->mi_rootfh, NULL, &mfname, NULL,
28247c478bd9Sstevel@tonic-gate 	    VFTOMI4(vfsp), CRED(), gethrtime());
28257c478bd9Sstevel@tonic-gate 
28267c478bd9Sstevel@tonic-gate 	if (VTOR4(vp)->r_flags & R4STALE) {
28277c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
28287c478bd9Sstevel@tonic-gate 		return (ENOENT);
28297c478bd9Sstevel@tonic-gate 	}
28307c478bd9Sstevel@tonic-gate 
28317c478bd9Sstevel@tonic-gate 	ASSERT(vp->v_type == VNON || vp->v_type == mi->mi_type);
28327c478bd9Sstevel@tonic-gate 
28337c478bd9Sstevel@tonic-gate 	vp->v_type = mi->mi_type;
28347c478bd9Sstevel@tonic-gate 
28357c478bd9Sstevel@tonic-gate 	*vpp = vp;
28367c478bd9Sstevel@tonic-gate 
28377c478bd9Sstevel@tonic-gate 	return (0);
28387c478bd9Sstevel@tonic-gate }
28397c478bd9Sstevel@tonic-gate 
28407c478bd9Sstevel@tonic-gate static int
nfs4_statfs_otw(vnode_t * vp,struct statvfs64 * sbp,cred_t * cr)28417c478bd9Sstevel@tonic-gate nfs4_statfs_otw(vnode_t *vp, struct statvfs64 *sbp, cred_t *cr)
28427c478bd9Sstevel@tonic-gate {
28437c478bd9Sstevel@tonic-gate 	int error;
28447c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t gar;
28457c478bd9Sstevel@tonic-gate 	nfs4_ga_ext_res_t ger;
28467c478bd9Sstevel@tonic-gate 
28477c478bd9Sstevel@tonic-gate 	gar.n4g_ext_res = &ger;
28487c478bd9Sstevel@tonic-gate 
28497c478bd9Sstevel@tonic-gate 	if (error = nfs4_attr_otw(vp, TAG_FSINFO, &gar,
28507c478bd9Sstevel@tonic-gate 	    NFS4_STATFS_ATTR_MASK, cr))
28517c478bd9Sstevel@tonic-gate 		return (error);
28527c478bd9Sstevel@tonic-gate 
28537c478bd9Sstevel@tonic-gate 	*sbp = gar.n4g_ext_res->n4g_sb;
28547c478bd9Sstevel@tonic-gate 
28557c478bd9Sstevel@tonic-gate 	return (0);
28567c478bd9Sstevel@tonic-gate }
28577c478bd9Sstevel@tonic-gate 
28587c478bd9Sstevel@tonic-gate /*
28597c478bd9Sstevel@tonic-gate  * Get file system statistics.
28607c478bd9Sstevel@tonic-gate  */
28617c478bd9Sstevel@tonic-gate static int
nfs4_statvfs(vfs_t * vfsp,struct statvfs64 * sbp)28627c478bd9Sstevel@tonic-gate nfs4_statvfs(vfs_t *vfsp, struct statvfs64 *sbp)
28637c478bd9Sstevel@tonic-gate {
28647c478bd9Sstevel@tonic-gate 	int error;
28657c478bd9Sstevel@tonic-gate 	vnode_t *vp;
28667c478bd9Sstevel@tonic-gate 	cred_t *cr;
28677c478bd9Sstevel@tonic-gate 
28687c478bd9Sstevel@tonic-gate 	error = nfs4_root(vfsp, &vp);
28697c478bd9Sstevel@tonic-gate 	if (error)
28707c478bd9Sstevel@tonic-gate 		return (error);
28717c478bd9Sstevel@tonic-gate 
28727c478bd9Sstevel@tonic-gate 	cr = CRED();
28737c478bd9Sstevel@tonic-gate 
28747c478bd9Sstevel@tonic-gate 	error = nfs4_statfs_otw(vp, sbp, cr);
28757c478bd9Sstevel@tonic-gate 	if (!error) {
28767c478bd9Sstevel@tonic-gate 		(void) strncpy(sbp->f_basetype,
28777c478bd9Sstevel@tonic-gate 		    vfssw[vfsp->vfs_fstype].vsw_name, FSTYPSZ);
28787c478bd9Sstevel@tonic-gate 		sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
28797c478bd9Sstevel@tonic-gate 	} else {
28807c478bd9Sstevel@tonic-gate 		nfs4_purge_stale_fh(error, vp, cr);
28817c478bd9Sstevel@tonic-gate 	}
28827c478bd9Sstevel@tonic-gate 
28837c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
28847c478bd9Sstevel@tonic-gate 
28857c478bd9Sstevel@tonic-gate 	return (error);
28867c478bd9Sstevel@tonic-gate }
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate static kmutex_t nfs4_syncbusy;
28897c478bd9Sstevel@tonic-gate 
28907c478bd9Sstevel@tonic-gate /*
28917c478bd9Sstevel@tonic-gate  * Flush dirty nfs files for file system vfsp.
28927c478bd9Sstevel@tonic-gate  * If vfsp == NULL, all nfs files are flushed.
28937c478bd9Sstevel@tonic-gate  *
28947c478bd9Sstevel@tonic-gate  * SYNC_CLOSE in flag is passed to us to
28957c478bd9Sstevel@tonic-gate  * indicate that we are shutting down and or
28967c478bd9Sstevel@tonic-gate  * rebooting.
28977c478bd9Sstevel@tonic-gate  */
28987c478bd9Sstevel@tonic-gate static int
nfs4_sync(vfs_t * vfsp,short flag,cred_t * cr)28997c478bd9Sstevel@tonic-gate nfs4_sync(vfs_t *vfsp, short flag, cred_t *cr)
29007c478bd9Sstevel@tonic-gate {
29017c478bd9Sstevel@tonic-gate 	/*
29027c478bd9Sstevel@tonic-gate 	 * Cross-zone calls are OK here, since this translates to a
29037c478bd9Sstevel@tonic-gate 	 * VOP_PUTPAGE(B_ASYNC), which gets picked up by the right zone.
29047c478bd9Sstevel@tonic-gate 	 */
29057c478bd9Sstevel@tonic-gate 	if (!(flag & SYNC_ATTR) && mutex_tryenter(&nfs4_syncbusy) != 0) {
29067c478bd9Sstevel@tonic-gate 		r4flush(vfsp, cr);
29077c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_syncbusy);
29087c478bd9Sstevel@tonic-gate 	}
29097c478bd9Sstevel@tonic-gate 
29107c478bd9Sstevel@tonic-gate 	/*
29117c478bd9Sstevel@tonic-gate 	 * if SYNC_CLOSE is set then we know that
29127c478bd9Sstevel@tonic-gate 	 * the system is rebooting, mark the mntinfo
29137c478bd9Sstevel@tonic-gate 	 * for later examination.
29147c478bd9Sstevel@tonic-gate 	 */
29157c478bd9Sstevel@tonic-gate 	if (vfsp && (flag & SYNC_CLOSE)) {
29167c478bd9Sstevel@tonic-gate 		mntinfo4_t *mi;
29177c478bd9Sstevel@tonic-gate 
29187c478bd9Sstevel@tonic-gate 		mi = VFTOMI4(vfsp);
29197c478bd9Sstevel@tonic-gate 		if (!(mi->mi_flags & MI4_SHUTDOWN)) {
29207c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
29217c478bd9Sstevel@tonic-gate 			mi->mi_flags |= MI4_SHUTDOWN;
29227c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
29237c478bd9Sstevel@tonic-gate 		}
29247c478bd9Sstevel@tonic-gate 	}
29257c478bd9Sstevel@tonic-gate 	return (0);
29267c478bd9Sstevel@tonic-gate }
29277c478bd9Sstevel@tonic-gate 
29287c478bd9Sstevel@tonic-gate /*
29297c478bd9Sstevel@tonic-gate  * vget is difficult, if not impossible, to support in v4 because we don't
29307c478bd9Sstevel@tonic-gate  * know the parent directory or name, which makes it impossible to create a
29317c478bd9Sstevel@tonic-gate  * useful shadow vnode.  And we need the shadow vnode for things like
29327c478bd9Sstevel@tonic-gate  * OPEN.
29337c478bd9Sstevel@tonic-gate  */
29347c478bd9Sstevel@tonic-gate 
29357c478bd9Sstevel@tonic-gate /* ARGSUSED */
29367c478bd9Sstevel@tonic-gate /*
29377c478bd9Sstevel@tonic-gate  * XXX Check nfs4_vget_pseudo() for dependency.
29387c478bd9Sstevel@tonic-gate  */
29397c478bd9Sstevel@tonic-gate static int
nfs4_vget(vfs_t * vfsp,vnode_t ** vpp,fid_t * fidp)29407c478bd9Sstevel@tonic-gate nfs4_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
29417c478bd9Sstevel@tonic-gate {
29427c478bd9Sstevel@tonic-gate 	return (EREMOTE);
29437c478bd9Sstevel@tonic-gate }
29447c478bd9Sstevel@tonic-gate 
29457c478bd9Sstevel@tonic-gate /*
29467c478bd9Sstevel@tonic-gate  * nfs4_mountroot get called in the case where we are diskless booting.  All
29477c478bd9Sstevel@tonic-gate  * we need from here is the ability to get the server info and from there we
29487c478bd9Sstevel@tonic-gate  * can simply call nfs4_rootvp.
29497c478bd9Sstevel@tonic-gate  */
29507c478bd9Sstevel@tonic-gate /* ARGSUSED */
29517c478bd9Sstevel@tonic-gate static int
nfs4_mountroot(vfs_t * vfsp,whymountroot_t why)29527c478bd9Sstevel@tonic-gate nfs4_mountroot(vfs_t *vfsp, whymountroot_t why)
29537c478bd9Sstevel@tonic-gate {
29547c478bd9Sstevel@tonic-gate 	vnode_t *rtvp;
29557c478bd9Sstevel@tonic-gate 	char root_hostname[SYS_NMLN+1];
29567c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp;
29577c478bd9Sstevel@tonic-gate 	int error;
29587c478bd9Sstevel@tonic-gate 	int vfsflags;
29597c478bd9Sstevel@tonic-gate 	size_t size;
29607c478bd9Sstevel@tonic-gate 	char *root_path;
29617c478bd9Sstevel@tonic-gate 	struct pathname pn;
29627c478bd9Sstevel@tonic-gate 	char *name;
29637c478bd9Sstevel@tonic-gate 	cred_t *cr;
29647c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
29657c478bd9Sstevel@tonic-gate 	struct nfs_args args;		/* nfs mount arguments */
29667c478bd9Sstevel@tonic-gate 	static char token[10];
29677c478bd9Sstevel@tonic-gate 	nfs4_error_t n4e;
29687c478bd9Sstevel@tonic-gate 
29697c478bd9Sstevel@tonic-gate 	bzero(&args, sizeof (args));
29707c478bd9Sstevel@tonic-gate 
29717c478bd9Sstevel@tonic-gate 	/* do this BEFORE getfile which causes xid stamps to be initialized */
29727c478bd9Sstevel@tonic-gate 	clkset(-1L);		/* hack for now - until we get time svc? */
29737c478bd9Sstevel@tonic-gate 
29747c478bd9Sstevel@tonic-gate 	if (why == ROOT_REMOUNT) {
29757c478bd9Sstevel@tonic-gate 		/*
29767c478bd9Sstevel@tonic-gate 		 * Shouldn't happen.
29777c478bd9Sstevel@tonic-gate 		 */
29787c478bd9Sstevel@tonic-gate 		panic("nfs4_mountroot: why == ROOT_REMOUNT");
29797c478bd9Sstevel@tonic-gate 	}
29807c478bd9Sstevel@tonic-gate 
29817c478bd9Sstevel@tonic-gate 	if (why == ROOT_UNMOUNT) {
29827c478bd9Sstevel@tonic-gate 		/*
29837c478bd9Sstevel@tonic-gate 		 * Nothing to do for NFS.
29847c478bd9Sstevel@tonic-gate 		 */
29857c478bd9Sstevel@tonic-gate 		return (0);
29867c478bd9Sstevel@tonic-gate 	}
29877c478bd9Sstevel@tonic-gate 
29887c478bd9Sstevel@tonic-gate 	/*
29897c478bd9Sstevel@tonic-gate 	 * why == ROOT_INIT
29907c478bd9Sstevel@tonic-gate 	 */
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate 	name = token;
29937c478bd9Sstevel@tonic-gate 	*name = 0;
29947c478bd9Sstevel@tonic-gate 	(void) getfsname("root", name, sizeof (token));
29957c478bd9Sstevel@tonic-gate 
29967c478bd9Sstevel@tonic-gate 	pn_alloc(&pn);
29977c478bd9Sstevel@tonic-gate 	root_path = pn.pn_path;
29987c478bd9Sstevel@tonic-gate 
29997c478bd9Sstevel@tonic-gate 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
30007c478bd9Sstevel@tonic-gate 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
30017c478bd9Sstevel@tonic-gate 	svp->sv_knconf = kmem_zalloc(sizeof (*svp->sv_knconf), KM_SLEEP);
30027c478bd9Sstevel@tonic-gate 	svp->sv_knconf->knc_protofmly = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
30037c478bd9Sstevel@tonic-gate 	svp->sv_knconf->knc_proto = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
30047c478bd9Sstevel@tonic-gate 
30057c478bd9Sstevel@tonic-gate 	/*
30067c478bd9Sstevel@tonic-gate 	 * Get server address
30077c478bd9Sstevel@tonic-gate 	 * Get the root path
30087c478bd9Sstevel@tonic-gate 	 * Get server's transport
30097c478bd9Sstevel@tonic-gate 	 * Get server's hostname
30107c478bd9Sstevel@tonic-gate 	 * Get options
30117c478bd9Sstevel@tonic-gate 	 */
30127c478bd9Sstevel@tonic-gate 	args.addr = &svp->sv_addr;
30137c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
30147c478bd9Sstevel@tonic-gate 	args.fh = (char *)&svp->sv_fhandle;
30157c478bd9Sstevel@tonic-gate 	args.knconf = svp->sv_knconf;
30167c478bd9Sstevel@tonic-gate 	args.hostname = root_hostname;
30177c478bd9Sstevel@tonic-gate 	vfsflags = 0;
30187c478bd9Sstevel@tonic-gate 	if (error = mount_root(*name ? name : "root", root_path, NFS_V4,
30197c478bd9Sstevel@tonic-gate 	    &args, &vfsflags)) {
30207c478bd9Sstevel@tonic-gate 		if (error == EPROTONOSUPPORT)
30217c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN, "nfs4_mountroot: "
30227c478bd9Sstevel@tonic-gate 			    "mount_root failed: server doesn't support NFS V4");
30237c478bd9Sstevel@tonic-gate 		else
30247c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
30257c478bd9Sstevel@tonic-gate 			    "nfs4_mountroot: mount_root failed: %m");
30267c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
30277c478bd9Sstevel@tonic-gate 		sv4_free(svp);
30287c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30297c478bd9Sstevel@tonic-gate 		return (error);
30307c478bd9Sstevel@tonic-gate 	}
30317c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
30327c478bd9Sstevel@tonic-gate 	svp->sv_hostnamelen = (int)(strlen(root_hostname) + 1);
30337c478bd9Sstevel@tonic-gate 	svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP);
30347c478bd9Sstevel@tonic-gate 	(void) strcpy(svp->sv_hostname, root_hostname);
30357c478bd9Sstevel@tonic-gate 
30367c478bd9Sstevel@tonic-gate 	svp->sv_pathlen = (int)(strlen(root_path) + 1);
30377c478bd9Sstevel@tonic-gate 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
30387c478bd9Sstevel@tonic-gate 	(void) strcpy(svp->sv_path, root_path);
30397c478bd9Sstevel@tonic-gate 
30407c478bd9Sstevel@tonic-gate 	/*
30417c478bd9Sstevel@tonic-gate 	 * Force root partition to always be mounted with AUTH_UNIX for now
30427c478bd9Sstevel@tonic-gate 	 */
30437c478bd9Sstevel@tonic-gate 	svp->sv_secdata = kmem_alloc(sizeof (*svp->sv_secdata), KM_SLEEP);
30447c478bd9Sstevel@tonic-gate 	svp->sv_secdata->secmod = AUTH_UNIX;
30457c478bd9Sstevel@tonic-gate 	svp->sv_secdata->rpcflavor = AUTH_UNIX;
30467c478bd9Sstevel@tonic-gate 	svp->sv_secdata->data = NULL;
30477c478bd9Sstevel@tonic-gate 
30487c478bd9Sstevel@tonic-gate 	cr = crgetcred();
30497c478bd9Sstevel@tonic-gate 	rtvp = NULL;
30507c478bd9Sstevel@tonic-gate 
30517c478bd9Sstevel@tonic-gate 	error = nfs4rootvp(&rtvp, vfsp, svp, args.flags, cr, global_zone);
30527c478bd9Sstevel@tonic-gate 
30537c478bd9Sstevel@tonic-gate 	if (error) {
30547c478bd9Sstevel@tonic-gate 		crfree(cr);
30557c478bd9Sstevel@tonic-gate 		pn_free(&pn);
3056ab7762b6Smaheshvs 		sv4_free(svp);
3057ab7762b6Smaheshvs 		return (error);
30587c478bd9Sstevel@tonic-gate 	}
30597c478bd9Sstevel@tonic-gate 
30607c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rtvp);
30617c478bd9Sstevel@tonic-gate 
30627c478bd9Sstevel@tonic-gate 	/*
30637c478bd9Sstevel@tonic-gate 	 * Send client id to the server, if necessary
30647c478bd9Sstevel@tonic-gate 	 */
30657c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&n4e);
30667c478bd9Sstevel@tonic-gate 	nfs4setclientid(mi, cr, FALSE, &n4e);
30677c478bd9Sstevel@tonic-gate 	error = n4e.error;
30687c478bd9Sstevel@tonic-gate 
30697c478bd9Sstevel@tonic-gate 	crfree(cr);
30707c478bd9Sstevel@tonic-gate 
30717c478bd9Sstevel@tonic-gate 	if (error) {
30727c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30737c478bd9Sstevel@tonic-gate 		goto errout;
30747c478bd9Sstevel@tonic-gate 	}
30757c478bd9Sstevel@tonic-gate 
30767c478bd9Sstevel@tonic-gate 	error = nfs4_setopts(rtvp, DATAMODEL_NATIVE, &args);
30777c478bd9Sstevel@tonic-gate 	if (error) {
30787c478bd9Sstevel@tonic-gate 		nfs_cmn_err(error, CE_WARN,
30797c478bd9Sstevel@tonic-gate 		    "nfs4_mountroot: invalid root mount options");
30807c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30817c478bd9Sstevel@tonic-gate 		goto errout;
30827c478bd9Sstevel@tonic-gate 	}
30837c478bd9Sstevel@tonic-gate 
30847c478bd9Sstevel@tonic-gate 	(void) vfs_lock_wait(vfsp);
30857c478bd9Sstevel@tonic-gate 	vfs_add(NULL, vfsp, vfsflags);
30867c478bd9Sstevel@tonic-gate 	vfs_unlock(vfsp);
30877c478bd9Sstevel@tonic-gate 
30887c478bd9Sstevel@tonic-gate 	size = strlen(svp->sv_hostname);
30897c478bd9Sstevel@tonic-gate 	(void) strcpy(rootfs.bo_name, svp->sv_hostname);
30907c478bd9Sstevel@tonic-gate 	rootfs.bo_name[size] = ':';
30917c478bd9Sstevel@tonic-gate 	(void) strcpy(&rootfs.bo_name[size + 1], root_path);
30927c478bd9Sstevel@tonic-gate 
30937c478bd9Sstevel@tonic-gate 	pn_free(&pn);
30947c478bd9Sstevel@tonic-gate 
30957c478bd9Sstevel@tonic-gate errout:
30967c478bd9Sstevel@tonic-gate 	if (error) {
30977c478bd9Sstevel@tonic-gate 		sv4_free(svp);
30987c478bd9Sstevel@tonic-gate 		nfs4_async_stop(vfsp);
30997c478bd9Sstevel@tonic-gate 		nfs4_async_manager_stop(vfsp);
31007c478bd9Sstevel@tonic-gate 	}
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 	if (rtvp != NULL)
31037c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
31047c478bd9Sstevel@tonic-gate 
31057c478bd9Sstevel@tonic-gate 	return (error);
31067c478bd9Sstevel@tonic-gate }
31077c478bd9Sstevel@tonic-gate 
31087c478bd9Sstevel@tonic-gate /*
31097c478bd9Sstevel@tonic-gate  * Initialization routine for VFS routines.  Should only be called once
31107c478bd9Sstevel@tonic-gate  */
31117c478bd9Sstevel@tonic-gate int
nfs4_vfsinit(void)31127c478bd9Sstevel@tonic-gate nfs4_vfsinit(void)
31137c478bd9Sstevel@tonic-gate {
31147c478bd9Sstevel@tonic-gate 	mutex_init(&nfs4_syncbusy, NULL, MUTEX_DEFAULT, NULL);
31157c478bd9Sstevel@tonic-gate 	nfs4setclientid_init();
3116b9238976Sth199096 	nfs4_ephemeral_init();
31177c478bd9Sstevel@tonic-gate 	return (0);
31187c478bd9Sstevel@tonic-gate }
31197c478bd9Sstevel@tonic-gate 
31207c478bd9Sstevel@tonic-gate void
nfs4_vfsfini(void)31217c478bd9Sstevel@tonic-gate nfs4_vfsfini(void)
31227c478bd9Sstevel@tonic-gate {
3123b9238976Sth199096 	nfs4_ephemeral_fini();
31247c478bd9Sstevel@tonic-gate 	nfs4setclientid_fini();
31257c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfs4_syncbusy);
31267c478bd9Sstevel@tonic-gate }
31277c478bd9Sstevel@tonic-gate 
31287c478bd9Sstevel@tonic-gate void
nfs4_freevfs(vfs_t * vfsp)31297c478bd9Sstevel@tonic-gate nfs4_freevfs(vfs_t *vfsp)
31307c478bd9Sstevel@tonic-gate {
31317c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
31327c478bd9Sstevel@tonic-gate 
313350a83466Sjwahlig 	/* need to release the initial hold */
31347c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
3135b87f76edSThomas Haynes 
3136b87f76edSThomas Haynes 	/*
3137b87f76edSThomas Haynes 	 * At this point, we can no longer reference the vfs
3138b87f76edSThomas Haynes 	 * and need to inform other holders of the reference
3139b87f76edSThomas Haynes 	 * to the mntinfo4_t.
3140b87f76edSThomas Haynes 	 */
3141b87f76edSThomas Haynes 	mi->mi_vfsp = NULL;
3142b87f76edSThomas Haynes 
314350a83466Sjwahlig 	MI4_RELE(mi);
31447c478bd9Sstevel@tonic-gate }
31457c478bd9Sstevel@tonic-gate 
31467c478bd9Sstevel@tonic-gate /*
31477c478bd9Sstevel@tonic-gate  * Client side SETCLIENTID and SETCLIENTID_CONFIRM
31487c478bd9Sstevel@tonic-gate  */
31497c478bd9Sstevel@tonic-gate struct nfs4_server nfs4_server_lst =
31507c478bd9Sstevel@tonic-gate 	{ &nfs4_server_lst, &nfs4_server_lst };
31517c478bd9Sstevel@tonic-gate 
31527c478bd9Sstevel@tonic-gate kmutex_t nfs4_server_lst_lock;
31537c478bd9Sstevel@tonic-gate 
31547c478bd9Sstevel@tonic-gate static void
nfs4setclientid_init(void)31557c478bd9Sstevel@tonic-gate nfs4setclientid_init(void)
31567c478bd9Sstevel@tonic-gate {
31577c478bd9Sstevel@tonic-gate 	mutex_init(&nfs4_server_lst_lock, NULL, MUTEX_DEFAULT, NULL);
31587c478bd9Sstevel@tonic-gate }
31597c478bd9Sstevel@tonic-gate 
31607c478bd9Sstevel@tonic-gate static void
nfs4setclientid_fini(void)31617c478bd9Sstevel@tonic-gate nfs4setclientid_fini(void)
31627c478bd9Sstevel@tonic-gate {
31637c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfs4_server_lst_lock);
31647c478bd9Sstevel@tonic-gate }
31657c478bd9Sstevel@tonic-gate 
31667c478bd9Sstevel@tonic-gate int nfs4_retry_sclid_delay = NFS4_RETRY_SCLID_DELAY;
31677c478bd9Sstevel@tonic-gate int nfs4_num_sclid_retries = NFS4_NUM_SCLID_RETRIES;
31687c478bd9Sstevel@tonic-gate 
31697c478bd9Sstevel@tonic-gate /*
31707c478bd9Sstevel@tonic-gate  * Set the clientid for the server for "mi".  No-op if the clientid is
31717c478bd9Sstevel@tonic-gate  * already set.
31727c478bd9Sstevel@tonic-gate  *
31737c478bd9Sstevel@tonic-gate  * The recovery boolean should be set to TRUE if this function was called
3174a092743bSek110237  * by the recovery code, and FALSE otherwise.  This is used to determine
3175a092743bSek110237  * if we need to call nfs4_start/end_op as well as grab the mi_recovlock
3176a092743bSek110237  * for adding a mntinfo4_t to a nfs4_server_t.
31777c478bd9Sstevel@tonic-gate  *
31787c478bd9Sstevel@tonic-gate  * Error is returned via 'n4ep'.  If there was a 'n4ep->stat' error, then
31797c478bd9Sstevel@tonic-gate  * 'n4ep->error' is set to geterrno4(n4ep->stat).
31807c478bd9Sstevel@tonic-gate  */
31817c478bd9Sstevel@tonic-gate void
nfs4setclientid(mntinfo4_t * mi,cred_t * cr,bool_t recovery,nfs4_error_t * n4ep)31827c478bd9Sstevel@tonic-gate nfs4setclientid(mntinfo4_t *mi, cred_t *cr, bool_t recovery, nfs4_error_t *n4ep)
31837c478bd9Sstevel@tonic-gate {
31847c478bd9Sstevel@tonic-gate 	struct nfs4_server *np;
31857c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp = mi->mi_curr_serv;
31867c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
31877c478bd9Sstevel@tonic-gate 	int num_retries = 0;
3188f64c4ae1Sdm120769 	bool_t retry;
31897c478bd9Sstevel@tonic-gate 	cred_t *lcr = NULL;
31907c478bd9Sstevel@tonic-gate 	int retry_inuse = 1; /* only retry once on NFS4ERR_CLID_INUSE */
31917c478bd9Sstevel@tonic-gate 	time_t lease_time = 0;
31927c478bd9Sstevel@tonic-gate 
31937c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
31947c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
31957c478bd9Sstevel@tonic-gate 	ASSERT(n4ep != NULL);
31967c478bd9Sstevel@tonic-gate 
31977c478bd9Sstevel@tonic-gate recov_retry:
3198f64c4ae1Sdm120769 	retry = FALSE;
31997c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(n4ep);
3200a092743bSek110237 	if (!recovery)
3201a092743bSek110237 		(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
3202a092743bSek110237 
3203f86c6ccaSdm120769 	mutex_enter(&nfs4_server_lst_lock);
3204f86c6ccaSdm120769 	np = servinfo4_to_nfs4_server(svp); /* This locks np if it is found */
3205f86c6ccaSdm120769 	mutex_exit(&nfs4_server_lst_lock);
3206f86c6ccaSdm120769 	if (!np) {
3207f86c6ccaSdm120769 		struct nfs4_server *tnp;
3208f86c6ccaSdm120769 		np = new_nfs4_server(svp, cr);
320916237317Sdm120769 		mutex_enter(&np->s_lock);
32107c478bd9Sstevel@tonic-gate 
3211f86c6ccaSdm120769 		mutex_enter(&nfs4_server_lst_lock);
3212f86c6ccaSdm120769 		tnp = servinfo4_to_nfs4_server(svp);
3213f86c6ccaSdm120769 		if (tnp) {
32147c478bd9Sstevel@tonic-gate 			/*
3215f86c6ccaSdm120769 			 * another thread snuck in and put server on list.
3216f86c6ccaSdm120769 			 * since we aren't adding it to the nfs4_server_list
3217f86c6ccaSdm120769 			 * we need to set the ref count to 0 and destroy it.
32187c478bd9Sstevel@tonic-gate 			 */
3219f86c6ccaSdm120769 			np->s_refcnt = 0;
3220f86c6ccaSdm120769 			destroy_nfs4_server(np);
3221f86c6ccaSdm120769 			np = tnp;
3222f86c6ccaSdm120769 		} else {
32237c478bd9Sstevel@tonic-gate 			/*
3224f86c6ccaSdm120769 			 * do not give list a reference until everything
3225f86c6ccaSdm120769 			 * succeeds
32267c478bd9Sstevel@tonic-gate 			 */
3227f86c6ccaSdm120769 			insque(np, &nfs4_server_lst);
3228f86c6ccaSdm120769 		}
3229f86c6ccaSdm120769 		mutex_exit(&nfs4_server_lst_lock);
3230f86c6ccaSdm120769 	}
3231f86c6ccaSdm120769 	ASSERT(MUTEX_HELD(&np->s_lock));
3232f86c6ccaSdm120769 	/*
3233f86c6ccaSdm120769 	 * If we find the server already has N4S_CLIENTID_SET, then
3234f86c6ccaSdm120769 	 * just return, we've already done SETCLIENTID to that server
3235f86c6ccaSdm120769 	 */
3236f86c6ccaSdm120769 	if (np->s_flags & N4S_CLIENTID_SET) {
32377c478bd9Sstevel@tonic-gate 		/* add mi to np's mntinfo4_list */
32387c478bd9Sstevel@tonic-gate 		nfs4_add_mi_to_server(np, mi);
3239a092743bSek110237 		if (!recovery)
3240a092743bSek110237 			nfs_rw_exit(&mi->mi_recovlock);
32417c478bd9Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
32427c478bd9Sstevel@tonic-gate 		nfs4_server_rele(np);
32437c478bd9Sstevel@tonic-gate 		return;
32447c478bd9Sstevel@tonic-gate 	}
3245f86c6ccaSdm120769 	mutex_exit(&np->s_lock);
3246f86c6ccaSdm120769 
32477c478bd9Sstevel@tonic-gate 
3248a092743bSek110237 	/*
3249a092743bSek110237 	 * Drop the mi_recovlock since nfs4_start_op will
3250a092743bSek110237 	 * acquire it again for us.
3251a092743bSek110237 	 */
3252f86c6ccaSdm120769 	if (!recovery) {
3253a092743bSek110237 		nfs_rw_exit(&mi->mi_recovlock);
3254a092743bSek110237 
32557c478bd9Sstevel@tonic-gate 		n4ep->error = nfs4_start_op(mi, NULL, NULL, &recov_state);
32567c478bd9Sstevel@tonic-gate 		if (n4ep->error) {
32577c478bd9Sstevel@tonic-gate 			nfs4_server_rele(np);
32587c478bd9Sstevel@tonic-gate 			return;
32597c478bd9Sstevel@tonic-gate 		}
32607c478bd9Sstevel@tonic-gate 	}
32617c478bd9Sstevel@tonic-gate 
32627c478bd9Sstevel@tonic-gate 	mutex_enter(&np->s_lock);
3263f86c6ccaSdm120769 	while (np->s_flags & N4S_CLIENTID_PEND) {
3264f86c6ccaSdm120769 		if (!cv_wait_sig(&np->s_clientid_pend, &np->s_lock)) {
3265f86c6ccaSdm120769 			mutex_exit(&np->s_lock);
3266f86c6ccaSdm120769 			nfs4_server_rele(np);
3267f86c6ccaSdm120769 			if (!recovery)
3268f86c6ccaSdm120769 				nfs4_end_op(mi, NULL, NULL, &recov_state,
3269f86c6ccaSdm120769 				    recovery);
3270f86c6ccaSdm120769 			n4ep->error = EINTR;
3271f86c6ccaSdm120769 			return;
3272f86c6ccaSdm120769 		}
3273f86c6ccaSdm120769 	}
3274f86c6ccaSdm120769 
3275f86c6ccaSdm120769 	if (np->s_flags & N4S_CLIENTID_SET) {
3276f86c6ccaSdm120769 		/* XXX copied/pasted from above */
3277f86c6ccaSdm120769 		/* add mi to np's mntinfo4_list */
3278f86c6ccaSdm120769 		nfs4_add_mi_to_server(np, mi);
3279f86c6ccaSdm120769 		mutex_exit(&np->s_lock);
3280f86c6ccaSdm120769 		nfs4_server_rele(np);
3281f86c6ccaSdm120769 		if (!recovery)
3282f86c6ccaSdm120769 			nfs4_end_op(mi, NULL, NULL, &recov_state, recovery);
3283f86c6ccaSdm120769 		return;
3284f86c6ccaSdm120769 	}
32857c478bd9Sstevel@tonic-gate 
32867c478bd9Sstevel@tonic-gate 	/*
32877c478bd9Sstevel@tonic-gate 	 * Reset the N4S_CB_PINGED flag. This is used to
32887c478bd9Sstevel@tonic-gate 	 * indicate if we have received a CB_NULL from the
32897c478bd9Sstevel@tonic-gate 	 * server. Also we reset the waiter flag.
32907c478bd9Sstevel@tonic-gate 	 */
32917c478bd9Sstevel@tonic-gate 	np->s_flags &= ~(N4S_CB_PINGED | N4S_CB_WAITER);
3292f86c6ccaSdm120769 	/* any failure must now clear this flag */
3293f86c6ccaSdm120769 	np->s_flags |= N4S_CLIENTID_PEND;
32947c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
32957c478bd9Sstevel@tonic-gate 	nfs4setclientid_otw(mi, svp, cr, np, n4ep, &retry_inuse);
32967c478bd9Sstevel@tonic-gate 
32977c478bd9Sstevel@tonic-gate 	if (n4ep->error == EACCES) {
32987c478bd9Sstevel@tonic-gate 		/*
32997c478bd9Sstevel@tonic-gate 		 * If the uid is set then set the creds for secure mounts
33007c478bd9Sstevel@tonic-gate 		 * by proxy processes such as automountd.
33017c478bd9Sstevel@tonic-gate 		 */
33027c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
33037c478bd9Sstevel@tonic-gate 		if (svp->sv_secdata->uid != 0) {
33047c478bd9Sstevel@tonic-gate 			lcr = crdup(cr);
33057c478bd9Sstevel@tonic-gate 			(void) crsetugid(lcr, svp->sv_secdata->uid,
33067c478bd9Sstevel@tonic-gate 			    crgetgid(cr));
33077c478bd9Sstevel@tonic-gate 		}
33087c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
33097c478bd9Sstevel@tonic-gate 
3310f86c6ccaSdm120769 		if (lcr != NULL) {
3311f86c6ccaSdm120769 			mutex_enter(&np->s_lock);
3312f86c6ccaSdm120769 			crfree(np->s_cred);
3313f86c6ccaSdm120769 			np->s_cred = lcr;
3314f86c6ccaSdm120769 			mutex_exit(&np->s_lock);
33157c478bd9Sstevel@tonic-gate 			nfs4setclientid_otw(mi, svp, lcr, np, n4ep,
33167c478bd9Sstevel@tonic-gate 			    &retry_inuse);
33177c478bd9Sstevel@tonic-gate 		}
3318f86c6ccaSdm120769 	}
3319f86c6ccaSdm120769 	mutex_enter(&np->s_lock);
33207c478bd9Sstevel@tonic-gate 	lease_time = np->s_lease_time;
3321f86c6ccaSdm120769 	np->s_flags &= ~N4S_CLIENTID_PEND;
33227c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
33237c478bd9Sstevel@tonic-gate 
33247c478bd9Sstevel@tonic-gate 	if (n4ep->error != 0 || n4ep->stat != NFS4_OK) {
33257c478bd9Sstevel@tonic-gate 		/*
33267c478bd9Sstevel@tonic-gate 		 * Start recovery if failover is a possibility.  If
33277c478bd9Sstevel@tonic-gate 		 * invoked by the recovery thread itself, then just
33287c478bd9Sstevel@tonic-gate 		 * return and let it handle the failover first.  NB:
33297c478bd9Sstevel@tonic-gate 		 * recovery is not allowed if the mount is in progress
33307c478bd9Sstevel@tonic-gate 		 * since the infrastructure is not sufficiently setup
33317c478bd9Sstevel@tonic-gate 		 * to allow it.  Just return the error (after suitable
33327c478bd9Sstevel@tonic-gate 		 * retries).
33337c478bd9Sstevel@tonic-gate 		 */
33347c478bd9Sstevel@tonic-gate 		if (FAILOVER_MOUNT4(mi) && nfs4_try_failover(n4ep)) {
33357c478bd9Sstevel@tonic-gate 			(void) nfs4_start_recovery(n4ep, mi, NULL,
33362f172c55SRobert Thurlow 			    NULL, NULL, NULL, OP_SETCLIENTID, NULL, NULL, NULL);
33377c478bd9Sstevel@tonic-gate 			/*
33387c478bd9Sstevel@tonic-gate 			 * Don't retry here, just return and let
33397c478bd9Sstevel@tonic-gate 			 * recovery take over.
33407c478bd9Sstevel@tonic-gate 			 */
33417c478bd9Sstevel@tonic-gate 			if (recovery)
33427c478bd9Sstevel@tonic-gate 				retry = FALSE;
33437c478bd9Sstevel@tonic-gate 		} else if (nfs4_rpc_retry_error(n4ep->error) ||
33447c478bd9Sstevel@tonic-gate 		    n4ep->stat == NFS4ERR_RESOURCE ||
33457c478bd9Sstevel@tonic-gate 		    n4ep->stat == NFS4ERR_STALE_CLIENTID) {
33467c478bd9Sstevel@tonic-gate 
33477c478bd9Sstevel@tonic-gate 			retry = TRUE;
33487c478bd9Sstevel@tonic-gate 			/*
33497c478bd9Sstevel@tonic-gate 			 * Always retry if in recovery or once had
33507c478bd9Sstevel@tonic-gate 			 * contact with the server (but now it's
33517c478bd9Sstevel@tonic-gate 			 * overloaded).
33527c478bd9Sstevel@tonic-gate 			 */
33537c478bd9Sstevel@tonic-gate 			if (recovery == TRUE ||
33547c478bd9Sstevel@tonic-gate 			    n4ep->error == ETIMEDOUT ||
33557c478bd9Sstevel@tonic-gate 			    n4ep->error == ECONNRESET)
33567c478bd9Sstevel@tonic-gate 				num_retries = 0;
33577c478bd9Sstevel@tonic-gate 		} else if (retry_inuse && n4ep->error == 0 &&
33587c478bd9Sstevel@tonic-gate 		    n4ep->stat == NFS4ERR_CLID_INUSE) {
33597c478bd9Sstevel@tonic-gate 			retry = TRUE;
33607c478bd9Sstevel@tonic-gate 			num_retries = 0;
33617c478bd9Sstevel@tonic-gate 		}
3362f86c6ccaSdm120769 	} else {
3363f64c4ae1Sdm120769 		/*
3364f64c4ae1Sdm120769 		 * Since everything succeeded give the list a reference count if
3365f64c4ae1Sdm120769 		 * it hasn't been given one by add_new_nfs4_server() or if this
3366f64c4ae1Sdm120769 		 * is not a recovery situation in which case it is already on
3367f64c4ae1Sdm120769 		 * the list.
3368f64c4ae1Sdm120769 		 */
3369f86c6ccaSdm120769 		mutex_enter(&np->s_lock);
3370f64c4ae1Sdm120769 		if ((np->s_flags & N4S_INSERTED) == 0) {
3371f86c6ccaSdm120769 			np->s_refcnt++;
3372f64c4ae1Sdm120769 			np->s_flags |= N4S_INSERTED;
3373f64c4ae1Sdm120769 		}
3374f86c6ccaSdm120769 		mutex_exit(&np->s_lock);
33757c478bd9Sstevel@tonic-gate 	}
33767c478bd9Sstevel@tonic-gate 
33777c478bd9Sstevel@tonic-gate 	if (!recovery)
33787c478bd9Sstevel@tonic-gate 		nfs4_end_op(mi, NULL, NULL, &recov_state, recovery);
3379f86c6ccaSdm120769 
33807c478bd9Sstevel@tonic-gate 
33817c478bd9Sstevel@tonic-gate 	if (retry && num_retries++ < nfs4_num_sclid_retries) {
33827c478bd9Sstevel@tonic-gate 		if (retry_inuse) {
33837c478bd9Sstevel@tonic-gate 			delay(SEC_TO_TICK(lease_time + nfs4_retry_sclid_delay));
33847c478bd9Sstevel@tonic-gate 			retry_inuse = 0;
33857c478bd9Sstevel@tonic-gate 		} else
33867c478bd9Sstevel@tonic-gate 			delay(SEC_TO_TICK(nfs4_retry_sclid_delay));
3387f86c6ccaSdm120769 
3388f86c6ccaSdm120769 		nfs4_server_rele(np);
33897c478bd9Sstevel@tonic-gate 		goto recov_retry;
33907c478bd9Sstevel@tonic-gate 	}
33917c478bd9Sstevel@tonic-gate 
3392f86c6ccaSdm120769 
33937c478bd9Sstevel@tonic-gate 	if (n4ep->error == 0)
33947c478bd9Sstevel@tonic-gate 		n4ep->error = geterrno4(n4ep->stat);
3395f86c6ccaSdm120769 
3396f86c6ccaSdm120769 	/* broadcast before release in case no other threads are waiting */
3397f86c6ccaSdm120769 	cv_broadcast(&np->s_clientid_pend);
3398f86c6ccaSdm120769 	nfs4_server_rele(np);
33997c478bd9Sstevel@tonic-gate }
34007c478bd9Sstevel@tonic-gate 
34017c478bd9Sstevel@tonic-gate int nfs4setclientid_otw_debug = 0;
34027c478bd9Sstevel@tonic-gate 
34037c478bd9Sstevel@tonic-gate /*
34047c478bd9Sstevel@tonic-gate  * This function handles the recovery of STALE_CLIENTID for SETCLIENTID_CONFRIM,
34057c478bd9Sstevel@tonic-gate  * but nothing else; the calling function must be designed to handle those
34067c478bd9Sstevel@tonic-gate  * other errors.
34077c478bd9Sstevel@tonic-gate  */
34087c478bd9Sstevel@tonic-gate static void
nfs4setclientid_otw(mntinfo4_t * mi,struct servinfo4 * svp,cred_t * cr,struct nfs4_server * np,nfs4_error_t * ep,int * retry_inusep)34097c478bd9Sstevel@tonic-gate nfs4setclientid_otw(mntinfo4_t *mi, struct servinfo4 *svp,  cred_t *cr,
34107c478bd9Sstevel@tonic-gate     struct nfs4_server *np, nfs4_error_t *ep, int *retry_inusep)
34117c478bd9Sstevel@tonic-gate {
34127c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
34137c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
34147c478bd9Sstevel@tonic-gate 	nfs_argop4 argop[3];
34157c478bd9Sstevel@tonic-gate 	SETCLIENTID4args *s_args;
34167c478bd9Sstevel@tonic-gate 	SETCLIENTID4resok *s_resok;
34177c478bd9Sstevel@tonic-gate 	int doqueue = 1;
34187c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t *garp = NULL;
34197c478bd9Sstevel@tonic-gate 	timespec_t prop_time, after_time;
34207c478bd9Sstevel@tonic-gate 	verifier4 verf;
34217c478bd9Sstevel@tonic-gate 	clientid4 tmp_clientid;
34227c478bd9Sstevel@tonic-gate 
3423f86c6ccaSdm120769 	ASSERT(!MUTEX_HELD(&np->s_lock));
34247c478bd9Sstevel@tonic-gate 
34257c478bd9Sstevel@tonic-gate 	args.ctag = TAG_SETCLIENTID;
34267c478bd9Sstevel@tonic-gate 
34277c478bd9Sstevel@tonic-gate 	args.array = argop;
34287c478bd9Sstevel@tonic-gate 	args.array_len = 3;
34297c478bd9Sstevel@tonic-gate 
34307c478bd9Sstevel@tonic-gate 	/* PUTROOTFH */
34317c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_PUTROOTFH;
34327c478bd9Sstevel@tonic-gate 
34337c478bd9Sstevel@tonic-gate 	/* GETATTR */
34347c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_GETATTR;
34357c478bd9Sstevel@tonic-gate 	argop[1].nfs_argop4_u.opgetattr.attr_request = FATTR4_LEASE_TIME_MASK;
34367c478bd9Sstevel@tonic-gate 	argop[1].nfs_argop4_u.opgetattr.mi = mi;
34377c478bd9Sstevel@tonic-gate 
34387c478bd9Sstevel@tonic-gate 	/* SETCLIENTID */
34397c478bd9Sstevel@tonic-gate 	argop[2].argop = OP_SETCLIENTID;
34407c478bd9Sstevel@tonic-gate 
34417c478bd9Sstevel@tonic-gate 	s_args = &argop[2].nfs_argop4_u.opsetclientid;
34427c478bd9Sstevel@tonic-gate 
3443f86c6ccaSdm120769 	mutex_enter(&np->s_lock);
3444f86c6ccaSdm120769 
34457c478bd9Sstevel@tonic-gate 	s_args->client.verifier = np->clidtosend.verifier;
34467c478bd9Sstevel@tonic-gate 	s_args->client.id_len = np->clidtosend.id_len;
34477c478bd9Sstevel@tonic-gate 	ASSERT(s_args->client.id_len <= NFS4_OPAQUE_LIMIT);
34487c478bd9Sstevel@tonic-gate 	s_args->client.id_val = np->clidtosend.id_val;
34497c478bd9Sstevel@tonic-gate 
34507c478bd9Sstevel@tonic-gate 	/*
34517c478bd9Sstevel@tonic-gate 	 * Callback needs to happen on non-RDMA transport
34527c478bd9Sstevel@tonic-gate 	 * Check if we have saved the original knetconfig
34537c478bd9Sstevel@tonic-gate 	 * if so, use that instead.
34547c478bd9Sstevel@tonic-gate 	 */
34557c478bd9Sstevel@tonic-gate 	if (svp->sv_origknconf != NULL)
34567c478bd9Sstevel@tonic-gate 		nfs4_cb_args(np, svp->sv_origknconf, s_args);
34577c478bd9Sstevel@tonic-gate 	else
34587c478bd9Sstevel@tonic-gate 		nfs4_cb_args(np, svp->sv_knconf, s_args);
34597c478bd9Sstevel@tonic-gate 
3460f86c6ccaSdm120769 	mutex_exit(&np->s_lock);
3461f86c6ccaSdm120769 
3462f86c6ccaSdm120769 	rfs4call(mi, &args, &res, cr, &doqueue, 0, ep);
34637c478bd9Sstevel@tonic-gate 
34647c478bd9Sstevel@tonic-gate 	if (ep->error)
34657c478bd9Sstevel@tonic-gate 		return;
34667c478bd9Sstevel@tonic-gate 
34677c478bd9Sstevel@tonic-gate 	/* getattr lease_time res */
3468e557fb2cSDai Ngo 	if ((res.array_len >= 2) &&
3469e557fb2cSDai Ngo 	    (res.array[1].nfs_resop4_u.opgetattr.status == NFS4_OK)) {
34707c478bd9Sstevel@tonic-gate 		garp = &res.array[1].nfs_resop4_u.opgetattr.ga_res;
34717c478bd9Sstevel@tonic-gate 
34727c478bd9Sstevel@tonic-gate #ifndef _LP64
34737c478bd9Sstevel@tonic-gate 		/*
34747c478bd9Sstevel@tonic-gate 		 * The 32 bit client cannot handle a lease time greater than
34757c478bd9Sstevel@tonic-gate 		 * (INT32_MAX/1000000).  This is due to the use of the
34767c478bd9Sstevel@tonic-gate 		 * lease_time in calls to drv_usectohz() in
34777c478bd9Sstevel@tonic-gate 		 * nfs4_renew_lease_thread().  The problem is that
34787c478bd9Sstevel@tonic-gate 		 * drv_usectohz() takes a time_t (which is just a long = 4
34797c478bd9Sstevel@tonic-gate 		 * bytes) as its parameter.  The lease_time is multiplied by
34807c478bd9Sstevel@tonic-gate 		 * 1000000 to convert seconds to usecs for the parameter.  If
34817c478bd9Sstevel@tonic-gate 		 * a number bigger than (INT32_MAX/1000000) is used then we
34827c478bd9Sstevel@tonic-gate 		 * overflow on the 32bit client.
34837c478bd9Sstevel@tonic-gate 		 */
34847c478bd9Sstevel@tonic-gate 		if (garp->n4g_ext_res->n4g_leasetime > (INT32_MAX/1000000)) {
34857c478bd9Sstevel@tonic-gate 			garp->n4g_ext_res->n4g_leasetime = INT32_MAX/1000000;
34867c478bd9Sstevel@tonic-gate 		}
34877c478bd9Sstevel@tonic-gate #endif
34887c478bd9Sstevel@tonic-gate 
3489f86c6ccaSdm120769 		mutex_enter(&np->s_lock);
34907c478bd9Sstevel@tonic-gate 		np->s_lease_time = garp->n4g_ext_res->n4g_leasetime;
34917c478bd9Sstevel@tonic-gate 
34927c478bd9Sstevel@tonic-gate 		/*
34937c478bd9Sstevel@tonic-gate 		 * Keep track of the lease period for the mi's
34947c478bd9Sstevel@tonic-gate 		 * mi_msg_list.  We need an appropiate time
34957c478bd9Sstevel@tonic-gate 		 * bound to associate past facts with a current
34967c478bd9Sstevel@tonic-gate 		 * event.  The lease period is perfect for this.
34977c478bd9Sstevel@tonic-gate 		 */
34987c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_msg_list_lock);
34997c478bd9Sstevel@tonic-gate 		mi->mi_lease_period = np->s_lease_time;
35007c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_msg_list_lock);
3501f86c6ccaSdm120769 		mutex_exit(&np->s_lock);
35027c478bd9Sstevel@tonic-gate 	}
35037c478bd9Sstevel@tonic-gate 
35047c478bd9Sstevel@tonic-gate 
35057c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_CLID_INUSE) {
35067c478bd9Sstevel@tonic-gate 		clientaddr4 *clid_inuse;
35077c478bd9Sstevel@tonic-gate 
35087c478bd9Sstevel@tonic-gate 		if (!(*retry_inusep)) {
35097c478bd9Sstevel@tonic-gate 			clid_inuse = &res.array->nfs_resop4_u.
35107c478bd9Sstevel@tonic-gate 			    opsetclientid.SETCLIENTID4res_u.client_using;
35117c478bd9Sstevel@tonic-gate 
35127c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
35137c478bd9Sstevel@tonic-gate 			    "NFS4 mount (SETCLIENTID failed)."
35147c478bd9Sstevel@tonic-gate 			    "  nfs4_client_id.id is in"
35157c478bd9Sstevel@tonic-gate 			    "use already by: r_netid<%s> r_addr<%s>",
35167c478bd9Sstevel@tonic-gate 			    clid_inuse->r_netid, clid_inuse->r_addr);
35177c478bd9Sstevel@tonic-gate 		}
35187c478bd9Sstevel@tonic-gate 
35197c478bd9Sstevel@tonic-gate 		/*
35207c478bd9Sstevel@tonic-gate 		 * XXX - The client should be more robust in its
35217c478bd9Sstevel@tonic-gate 		 * handling of clientid in use errors (regen another
35227c478bd9Sstevel@tonic-gate 		 * clientid and try again?)
35237c478bd9Sstevel@tonic-gate 		 */
35247c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
35257c478bd9Sstevel@tonic-gate 		return;
35267c478bd9Sstevel@tonic-gate 	}
35277c478bd9Sstevel@tonic-gate 
35287c478bd9Sstevel@tonic-gate 	if (res.status) {
35297c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
35307c478bd9Sstevel@tonic-gate 		return;
35317c478bd9Sstevel@tonic-gate 	}
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate 	s_resok = &res.array[2].nfs_resop4_u.
35347c478bd9Sstevel@tonic-gate 	    opsetclientid.SETCLIENTID4res_u.resok4;
35357c478bd9Sstevel@tonic-gate 
35367c478bd9Sstevel@tonic-gate 	tmp_clientid = s_resok->clientid;
35377c478bd9Sstevel@tonic-gate 
35387c478bd9Sstevel@tonic-gate 	verf = s_resok->setclientid_confirm;
35397c478bd9Sstevel@tonic-gate 
35407c478bd9Sstevel@tonic-gate #ifdef	DEBUG
35417c478bd9Sstevel@tonic-gate 	if (nfs4setclientid_otw_debug) {
35427c478bd9Sstevel@tonic-gate 		union {
35437c478bd9Sstevel@tonic-gate 			clientid4	clientid;
35447c478bd9Sstevel@tonic-gate 			int		foo[2];
35457c478bd9Sstevel@tonic-gate 		} cid;
35467c478bd9Sstevel@tonic-gate 
35477c478bd9Sstevel@tonic-gate 		cid.clientid = s_resok->clientid;
35487c478bd9Sstevel@tonic-gate 
35497c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
35507c478bd9Sstevel@tonic-gate 		"nfs4setclientid_otw: OK, clientid = %x,%x, "
35517c478bd9Sstevel@tonic-gate 		"verifier = %" PRIx64 "\n", cid.foo[0], cid.foo[1], verf);
35527c478bd9Sstevel@tonic-gate 	}
35537c478bd9Sstevel@tonic-gate #endif
35547c478bd9Sstevel@tonic-gate 
35557c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
35567c478bd9Sstevel@tonic-gate 
35577c478bd9Sstevel@tonic-gate 	/* Confirm the client id and get the lease_time attribute */
35587c478bd9Sstevel@tonic-gate 
35597c478bd9Sstevel@tonic-gate 	args.ctag = TAG_SETCLIENTID_CF;
35607c478bd9Sstevel@tonic-gate 
35617c478bd9Sstevel@tonic-gate 	args.array = argop;
35627c478bd9Sstevel@tonic-gate 	args.array_len = 1;
35637c478bd9Sstevel@tonic-gate 
35647c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_SETCLIENTID_CONFIRM;
35657c478bd9Sstevel@tonic-gate 
35667c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opsetclientid_confirm.clientid = tmp_clientid;
35677c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opsetclientid_confirm.setclientid_confirm = verf;
35687c478bd9Sstevel@tonic-gate 
35697c478bd9Sstevel@tonic-gate 	/* used to figure out RTT for np */
35707c478bd9Sstevel@tonic-gate 	gethrestime(&prop_time);
35717c478bd9Sstevel@tonic-gate 
35727c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setlientid_otw: "
35737c478bd9Sstevel@tonic-gate 	    "start time: %ld sec %ld nsec", prop_time.tv_sec,
35747c478bd9Sstevel@tonic-gate 	    prop_time.tv_nsec));
35757c478bd9Sstevel@tonic-gate 
35767c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, 0, ep);
35777c478bd9Sstevel@tonic-gate 
35787c478bd9Sstevel@tonic-gate 	gethrestime(&after_time);
3579f86c6ccaSdm120769 	mutex_enter(&np->s_lock);
35807c478bd9Sstevel@tonic-gate 	np->propagation_delay.tv_sec =
35817c478bd9Sstevel@tonic-gate 	    MAX(1, after_time.tv_sec - prop_time.tv_sec);
3582f86c6ccaSdm120769 	mutex_exit(&np->s_lock);
35837c478bd9Sstevel@tonic-gate 
35847c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setlcientid_otw: "
35857c478bd9Sstevel@tonic-gate 	    "finish time: %ld sec ", after_time.tv_sec));
35867c478bd9Sstevel@tonic-gate 
35877c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setclientid_otw: "
35887c478bd9Sstevel@tonic-gate 	    "propagation delay set to %ld sec",
35897c478bd9Sstevel@tonic-gate 	    np->propagation_delay.tv_sec));
35907c478bd9Sstevel@tonic-gate 
35917c478bd9Sstevel@tonic-gate 	if (ep->error)
35927c478bd9Sstevel@tonic-gate 		return;
35937c478bd9Sstevel@tonic-gate 
35947c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_CLID_INUSE) {
35957c478bd9Sstevel@tonic-gate 		clientaddr4 *clid_inuse;
35967c478bd9Sstevel@tonic-gate 
35977c478bd9Sstevel@tonic-gate 		if (!(*retry_inusep)) {
35987c478bd9Sstevel@tonic-gate 			clid_inuse = &res.array->nfs_resop4_u.
35997c478bd9Sstevel@tonic-gate 			    opsetclientid.SETCLIENTID4res_u.client_using;
36007c478bd9Sstevel@tonic-gate 
36017c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
36027c478bd9Sstevel@tonic-gate 			    "SETCLIENTID_CONFIRM failed.  "
36037c478bd9Sstevel@tonic-gate 			    "nfs4_client_id.id is in use already by: "
36047c478bd9Sstevel@tonic-gate 			    "r_netid<%s> r_addr<%s>",
36057c478bd9Sstevel@tonic-gate 			    clid_inuse->r_netid, clid_inuse->r_addr);
36067c478bd9Sstevel@tonic-gate 		}
36077c478bd9Sstevel@tonic-gate 
36087c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36097c478bd9Sstevel@tonic-gate 		return;
36107c478bd9Sstevel@tonic-gate 	}
36117c478bd9Sstevel@tonic-gate 
36127c478bd9Sstevel@tonic-gate 	if (res.status) {
36137c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36147c478bd9Sstevel@tonic-gate 		return;
36157c478bd9Sstevel@tonic-gate 	}
36167c478bd9Sstevel@tonic-gate 
3617f86c6ccaSdm120769 	mutex_enter(&np->s_lock);
36187c478bd9Sstevel@tonic-gate 	np->clientid = tmp_clientid;
36197c478bd9Sstevel@tonic-gate 	np->s_flags |= N4S_CLIENTID_SET;
36207c478bd9Sstevel@tonic-gate 
36217c478bd9Sstevel@tonic-gate 	/* Add mi to np's mntinfo4 list */
36227c478bd9Sstevel@tonic-gate 	nfs4_add_mi_to_server(np, mi);
36237c478bd9Sstevel@tonic-gate 
36247c478bd9Sstevel@tonic-gate 	if (np->lease_valid == NFS4_LEASE_NOT_STARTED) {
36257c478bd9Sstevel@tonic-gate 		/*
36267c478bd9Sstevel@tonic-gate 		 * Start lease management thread.
36277c478bd9Sstevel@tonic-gate 		 * Keep trying until we succeed.
36287c478bd9Sstevel@tonic-gate 		 */
36297c478bd9Sstevel@tonic-gate 
36307c478bd9Sstevel@tonic-gate 		np->s_refcnt++;		/* pass reference to thread */
36317c478bd9Sstevel@tonic-gate 		(void) zthread_create(NULL, 0, nfs4_renew_lease_thread, np, 0,
36327c478bd9Sstevel@tonic-gate 		    minclsyspri);
36337c478bd9Sstevel@tonic-gate 	}
3634f86c6ccaSdm120769 	mutex_exit(&np->s_lock);
36357c478bd9Sstevel@tonic-gate 
36367c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36377c478bd9Sstevel@tonic-gate }
36387c478bd9Sstevel@tonic-gate 
36397c478bd9Sstevel@tonic-gate /*
36407c478bd9Sstevel@tonic-gate  * Add mi to sp's mntinfo4_list if it isn't already in the list.  Makes
36417c478bd9Sstevel@tonic-gate  * mi's clientid the same as sp's.
36427c478bd9Sstevel@tonic-gate  * Assumes sp is locked down.
36437c478bd9Sstevel@tonic-gate  */
36447c478bd9Sstevel@tonic-gate void
nfs4_add_mi_to_server(nfs4_server_t * sp,mntinfo4_t * mi)36457c478bd9Sstevel@tonic-gate nfs4_add_mi_to_server(nfs4_server_t *sp, mntinfo4_t *mi)
36467c478bd9Sstevel@tonic-gate {
36477c478bd9Sstevel@tonic-gate 	mntinfo4_t *tmi;
36487c478bd9Sstevel@tonic-gate 	int in_list = 0;
36497c478bd9Sstevel@tonic-gate 
3650a092743bSek110237 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
3651a092743bSek110237 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
36527c478bd9Sstevel@tonic-gate 	ASSERT(sp != &nfs4_server_lst);
36537c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
36547c478bd9Sstevel@tonic-gate 
36557c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
36567c478bd9Sstevel@tonic-gate 	    "nfs4_add_mi_to_server: add mi %p to sp %p",
36577c478bd9Sstevel@tonic-gate 	    (void*)mi, (void*)sp));
36587c478bd9Sstevel@tonic-gate 
36597c478bd9Sstevel@tonic-gate 	for (tmi = sp->mntinfo4_list;
36607c478bd9Sstevel@tonic-gate 	    tmi != NULL;
36617c478bd9Sstevel@tonic-gate 	    tmi = tmi->mi_clientid_next) {
36627c478bd9Sstevel@tonic-gate 		if (tmi == mi) {
36637c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_lease_debug,
36647c478bd9Sstevel@tonic-gate 			    (CE_NOTE,
36657c478bd9Sstevel@tonic-gate 			    "nfs4_add_mi_to_server: mi in list"));
36667c478bd9Sstevel@tonic-gate 			in_list = 1;
36677c478bd9Sstevel@tonic-gate 		}
36687c478bd9Sstevel@tonic-gate 	}
36697c478bd9Sstevel@tonic-gate 
36707c478bd9Sstevel@tonic-gate 	/*
36717c478bd9Sstevel@tonic-gate 	 * First put a hold on the mntinfo4's vfsp so that references via
36727c478bd9Sstevel@tonic-gate 	 * mntinfo4_list will be valid.
36737c478bd9Sstevel@tonic-gate 	 */
36747c478bd9Sstevel@tonic-gate 	if (!in_list)
36757c478bd9Sstevel@tonic-gate 		VFS_HOLD(mi->mi_vfsp);
36767c478bd9Sstevel@tonic-gate 
36777c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4_add_mi_to_server: "
36787c478bd9Sstevel@tonic-gate 	    "hold vfs %p for mi: %p", (void*)mi->mi_vfsp, (void*)mi));
36797c478bd9Sstevel@tonic-gate 
36807c478bd9Sstevel@tonic-gate 	if (!in_list) {
36817c478bd9Sstevel@tonic-gate 		if (sp->mntinfo4_list)
36827c478bd9Sstevel@tonic-gate 			sp->mntinfo4_list->mi_clientid_prev = mi;
36837c478bd9Sstevel@tonic-gate 		mi->mi_clientid_next = sp->mntinfo4_list;
36843b895386SPavel Filipensky 		mi->mi_srv = sp;
36857c478bd9Sstevel@tonic-gate 		sp->mntinfo4_list = mi;
36867c478bd9Sstevel@tonic-gate 		mi->mi_srvsettime = gethrestime_sec();
36873b895386SPavel Filipensky 		mi->mi_srvset_cnt++;
36887c478bd9Sstevel@tonic-gate 	}
36897c478bd9Sstevel@tonic-gate 
36907c478bd9Sstevel@tonic-gate 	/* set mi's clientid to that of sp's for later matching */
36917c478bd9Sstevel@tonic-gate 	mi->mi_clientid = sp->clientid;
36927c478bd9Sstevel@tonic-gate 
36937c478bd9Sstevel@tonic-gate 	/*
36947c478bd9Sstevel@tonic-gate 	 * Update the clientid for any other mi's belonging to sp.  This
36957c478bd9Sstevel@tonic-gate 	 * must be done here while we hold sp->s_lock, so that
36967c478bd9Sstevel@tonic-gate 	 * find_nfs4_server() continues to work.
36977c478bd9Sstevel@tonic-gate 	 */
36987c478bd9Sstevel@tonic-gate 
36997c478bd9Sstevel@tonic-gate 	for (tmi = sp->mntinfo4_list;
37007c478bd9Sstevel@tonic-gate 	    tmi != NULL;
37017c478bd9Sstevel@tonic-gate 	    tmi = tmi->mi_clientid_next) {
37027c478bd9Sstevel@tonic-gate 		if (tmi != mi) {
37037c478bd9Sstevel@tonic-gate 			tmi->mi_clientid = sp->clientid;
37047c478bd9Sstevel@tonic-gate 		}
37057c478bd9Sstevel@tonic-gate 	}
37067c478bd9Sstevel@tonic-gate }
37077c478bd9Sstevel@tonic-gate 
37087c478bd9Sstevel@tonic-gate /*
37097c478bd9Sstevel@tonic-gate  * Remove the mi from sp's mntinfo4_list and release its reference.
37107c478bd9Sstevel@tonic-gate  * Exception: if mi still has open files, flag it for later removal (when
37117c478bd9Sstevel@tonic-gate  * all the files are closed).
37127c478bd9Sstevel@tonic-gate  *
37137c478bd9Sstevel@tonic-gate  * If this is the last mntinfo4 in sp's list then tell the lease renewal
37147c478bd9Sstevel@tonic-gate  * thread to exit.
37157c478bd9Sstevel@tonic-gate  */
37167c478bd9Sstevel@tonic-gate static void
nfs4_remove_mi_from_server_nolock(mntinfo4_t * mi,nfs4_server_t * sp)37177c478bd9Sstevel@tonic-gate nfs4_remove_mi_from_server_nolock(mntinfo4_t *mi, nfs4_server_t *sp)
37187c478bd9Sstevel@tonic-gate {
37197c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
37207c478bd9Sstevel@tonic-gate 	    "nfs4_remove_mi_from_server_nolock: remove mi %p from sp %p",
37217c478bd9Sstevel@tonic-gate 	    (void*)mi, (void*)sp));
37227c478bd9Sstevel@tonic-gate 
37237c478bd9Sstevel@tonic-gate 	ASSERT(sp != NULL);
37247c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
37257c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_open_files >= 0);
37267c478bd9Sstevel@tonic-gate 
37277c478bd9Sstevel@tonic-gate 	/*
37287c478bd9Sstevel@tonic-gate 	 * First make sure this mntinfo4 can be taken off of the list,
37297c478bd9Sstevel@tonic-gate 	 * ie: it doesn't have any open files remaining.
37307c478bd9Sstevel@tonic-gate 	 */
37317c478bd9Sstevel@tonic-gate 	if (mi->mi_open_files > 0) {
37327c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
37337c478bd9Sstevel@tonic-gate 		    "nfs4_remove_mi_from_server_nolock: don't "
37347c478bd9Sstevel@tonic-gate 		    "remove mi since it still has files open"));
37357c478bd9Sstevel@tonic-gate 
37367c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
37377c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_REMOVE_ON_LAST_CLOSE;
37387c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
37397c478bd9Sstevel@tonic-gate 		return;
37407c478bd9Sstevel@tonic-gate 	}
37417c478bd9Sstevel@tonic-gate 
374250a83466Sjwahlig 	VFS_HOLD(mi->mi_vfsp);
37437c478bd9Sstevel@tonic-gate 	remove_mi(sp, mi);
374450a83466Sjwahlig 	VFS_RELE(mi->mi_vfsp);
37457c478bd9Sstevel@tonic-gate 
37467c478bd9Sstevel@tonic-gate 	if (sp->mntinfo4_list == NULL) {
37477c478bd9Sstevel@tonic-gate 		/* last fs unmounted, kill the thread */
37487c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
37497c478bd9Sstevel@tonic-gate 		    "remove_mi_from_nfs4_server_nolock: kill the thread"));
37507c478bd9Sstevel@tonic-gate 		nfs4_mark_srv_dead(sp);
37517c478bd9Sstevel@tonic-gate 	}
37527c478bd9Sstevel@tonic-gate }
37537c478bd9Sstevel@tonic-gate 
37547c478bd9Sstevel@tonic-gate /*
37557c478bd9Sstevel@tonic-gate  * Remove mi from sp's mntinfo4_list and release the vfs reference.
37567c478bd9Sstevel@tonic-gate  */
37577c478bd9Sstevel@tonic-gate static void
remove_mi(nfs4_server_t * sp,mntinfo4_t * mi)37587c478bd9Sstevel@tonic-gate remove_mi(nfs4_server_t *sp, mntinfo4_t *mi)
37597c478bd9Sstevel@tonic-gate {
37607c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
37617c478bd9Sstevel@tonic-gate 
37627c478bd9Sstevel@tonic-gate 	/*
37637c478bd9Sstevel@tonic-gate 	 * We release a reference, and the caller must still have a
37647c478bd9Sstevel@tonic-gate 	 * reference.
37657c478bd9Sstevel@tonic-gate 	 */
37667c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_vfsp->vfs_count >= 2);
37677c478bd9Sstevel@tonic-gate 
37687c478bd9Sstevel@tonic-gate 	if (mi->mi_clientid_prev) {
37697c478bd9Sstevel@tonic-gate 		mi->mi_clientid_prev->mi_clientid_next = mi->mi_clientid_next;
37707c478bd9Sstevel@tonic-gate 	} else {
37717c478bd9Sstevel@tonic-gate 		/* This is the first mi in sp's mntinfo4_list */
37727c478bd9Sstevel@tonic-gate 		/*
37737c478bd9Sstevel@tonic-gate 		 * Make sure the first mntinfo4 in the list is the actual
37747c478bd9Sstevel@tonic-gate 		 * mntinfo4 passed in.
37757c478bd9Sstevel@tonic-gate 		 */
37767c478bd9Sstevel@tonic-gate 		ASSERT(sp->mntinfo4_list == mi);
37777c478bd9Sstevel@tonic-gate 
37787c478bd9Sstevel@tonic-gate 		sp->mntinfo4_list = mi->mi_clientid_next;
37797c478bd9Sstevel@tonic-gate 	}
37807c478bd9Sstevel@tonic-gate 	if (mi->mi_clientid_next)
37817c478bd9Sstevel@tonic-gate 		mi->mi_clientid_next->mi_clientid_prev = mi->mi_clientid_prev;
37827c478bd9Sstevel@tonic-gate 
37837c478bd9Sstevel@tonic-gate 	/* Now mark the mntinfo4's links as being removed */
37847c478bd9Sstevel@tonic-gate 	mi->mi_clientid_prev = mi->mi_clientid_next = NULL;
37853b895386SPavel Filipensky 	mi->mi_srv = NULL;
378622dc8f51SPavel Filipensky 	mi->mi_srvset_cnt++;
37877c478bd9Sstevel@tonic-gate 
37887c478bd9Sstevel@tonic-gate 	VFS_RELE(mi->mi_vfsp);
37897c478bd9Sstevel@tonic-gate }
37907c478bd9Sstevel@tonic-gate 
37917c478bd9Sstevel@tonic-gate /*
37927c478bd9Sstevel@tonic-gate  * Free all the entries in sp's mntinfo4_list.
37937c478bd9Sstevel@tonic-gate  */
37947c478bd9Sstevel@tonic-gate static void
remove_all_mi(nfs4_server_t * sp)37957c478bd9Sstevel@tonic-gate remove_all_mi(nfs4_server_t *sp)
37967c478bd9Sstevel@tonic-gate {
37977c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
37987c478bd9Sstevel@tonic-gate 
37997c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
38007c478bd9Sstevel@tonic-gate 
38017c478bd9Sstevel@tonic-gate 	while (sp->mntinfo4_list != NULL) {
38027c478bd9Sstevel@tonic-gate 		mi = sp->mntinfo4_list;
38037c478bd9Sstevel@tonic-gate 		/*
38047c478bd9Sstevel@tonic-gate 		 * Grab a reference in case there is only one left (which
38057c478bd9Sstevel@tonic-gate 		 * remove_mi() frees).
38067c478bd9Sstevel@tonic-gate 		 */
38077c478bd9Sstevel@tonic-gate 		VFS_HOLD(mi->mi_vfsp);
38087c478bd9Sstevel@tonic-gate 		remove_mi(sp, mi);
38097c478bd9Sstevel@tonic-gate 		VFS_RELE(mi->mi_vfsp);
38107c478bd9Sstevel@tonic-gate 	}
38117c478bd9Sstevel@tonic-gate }
38127c478bd9Sstevel@tonic-gate 
38137c478bd9Sstevel@tonic-gate /*
38147c478bd9Sstevel@tonic-gate  * Remove the mi from sp's mntinfo4_list as above, and rele the vfs.
38157c478bd9Sstevel@tonic-gate  *
38167c478bd9Sstevel@tonic-gate  * This version can be called with a null nfs4_server_t arg,
38177c478bd9Sstevel@tonic-gate  * and will either find the right one and handle locking, or
38187c478bd9Sstevel@tonic-gate  * do nothing because the mi wasn't added to an sp's mntinfo4_list.
38197c478bd9Sstevel@tonic-gate  */
38207c478bd9Sstevel@tonic-gate void
nfs4_remove_mi_from_server(mntinfo4_t * mi,nfs4_server_t * esp)38217c478bd9Sstevel@tonic-gate nfs4_remove_mi_from_server(mntinfo4_t *mi, nfs4_server_t *esp)
38227c478bd9Sstevel@tonic-gate {
38237c478bd9Sstevel@tonic-gate 	nfs4_server_t	*sp;
38247c478bd9Sstevel@tonic-gate 
38253b895386SPavel Filipensky 	if (esp) {
38263b895386SPavel Filipensky 		nfs4_remove_mi_from_server_nolock(mi, esp);
38273b895386SPavel Filipensky 		return;
38287c478bd9Sstevel@tonic-gate 	}
38293b895386SPavel Filipensky 
38303b895386SPavel Filipensky 	(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
383122dc8f51SPavel Filipensky 	if (sp = find_nfs4_server_all(mi, 1)) {
38323b895386SPavel Filipensky 		nfs4_remove_mi_from_server_nolock(mi, sp);
38333b895386SPavel Filipensky 		mutex_exit(&sp->s_lock);
383422dc8f51SPavel Filipensky 		nfs4_server_rele(sp);
38353b895386SPavel Filipensky 	}
38363b895386SPavel Filipensky 	nfs_rw_exit(&mi->mi_recovlock);
38377c478bd9Sstevel@tonic-gate }
38387c478bd9Sstevel@tonic-gate 
38397c478bd9Sstevel@tonic-gate /*
38407c478bd9Sstevel@tonic-gate  * Return TRUE if the given server has any non-unmounted filesystems.
38417c478bd9Sstevel@tonic-gate  */
38427c478bd9Sstevel@tonic-gate 
38437c478bd9Sstevel@tonic-gate bool_t
nfs4_fs_active(nfs4_server_t * sp)38447c478bd9Sstevel@tonic-gate nfs4_fs_active(nfs4_server_t *sp)
38457c478bd9Sstevel@tonic-gate {
38467c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
38477c478bd9Sstevel@tonic-gate 
38487c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
38497c478bd9Sstevel@tonic-gate 
38507c478bd9Sstevel@tonic-gate 	for (mi = sp->mntinfo4_list; mi != NULL; mi = mi->mi_clientid_next) {
38517c478bd9Sstevel@tonic-gate 		if (!(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED))
38527c478bd9Sstevel@tonic-gate 			return (TRUE);
38537c478bd9Sstevel@tonic-gate 	}
38547c478bd9Sstevel@tonic-gate 
38557c478bd9Sstevel@tonic-gate 	return (FALSE);
38567c478bd9Sstevel@tonic-gate }
38577c478bd9Sstevel@tonic-gate 
38587c478bd9Sstevel@tonic-gate /*
38597c478bd9Sstevel@tonic-gate  * Mark sp as finished and notify any waiters.
38607c478bd9Sstevel@tonic-gate  */
38617c478bd9Sstevel@tonic-gate 
38627c478bd9Sstevel@tonic-gate void
nfs4_mark_srv_dead(nfs4_server_t * sp)38637c478bd9Sstevel@tonic-gate nfs4_mark_srv_dead(nfs4_server_t *sp)
38647c478bd9Sstevel@tonic-gate {
38657c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
38667c478bd9Sstevel@tonic-gate 
38677c478bd9Sstevel@tonic-gate 	sp->s_thread_exit = NFS4_THREAD_EXIT;
38687c478bd9Sstevel@tonic-gate 	cv_broadcast(&sp->cv_thread_exit);
38697c478bd9Sstevel@tonic-gate }
38707c478bd9Sstevel@tonic-gate 
38717c478bd9Sstevel@tonic-gate /*
38727c478bd9Sstevel@tonic-gate  * Create a new nfs4_server_t structure.
38737c478bd9Sstevel@tonic-gate  * Returns new node unlocked and not in list, but with a reference count of
38747c478bd9Sstevel@tonic-gate  * 1.
38757c478bd9Sstevel@tonic-gate  */
38767c478bd9Sstevel@tonic-gate struct nfs4_server *
new_nfs4_server(struct servinfo4 * svp,cred_t * cr)38777c478bd9Sstevel@tonic-gate new_nfs4_server(struct servinfo4 *svp, cred_t *cr)
38787c478bd9Sstevel@tonic-gate {
38797c478bd9Sstevel@tonic-gate 	struct nfs4_server *np;
38807c478bd9Sstevel@tonic-gate 	timespec_t tt;
38817c478bd9Sstevel@tonic-gate 	union {
38827c478bd9Sstevel@tonic-gate 		struct {
38837c478bd9Sstevel@tonic-gate 			uint32_t sec;
38847c478bd9Sstevel@tonic-gate 			uint32_t subsec;
38857c478bd9Sstevel@tonic-gate 		} un_curtime;
38867c478bd9Sstevel@tonic-gate 		verifier4	un_verifier;
38877c478bd9Sstevel@tonic-gate 	} nfs4clientid_verifier;
38882f172c55SRobert Thurlow 	/*
38892f172c55SRobert Thurlow 	 * We change this ID string carefully and with the Solaris
38902f172c55SRobert Thurlow 	 * NFS server behaviour in mind.  "+referrals" indicates
38912f172c55SRobert Thurlow 	 * a client that can handle an NFSv4 referral.
38922f172c55SRobert Thurlow 	 */
38932f172c55SRobert Thurlow 	char id_val[] = "Solaris: %s, NFSv4 kernel client +referrals";
38947c478bd9Sstevel@tonic-gate 	int len;
38957c478bd9Sstevel@tonic-gate 
38967c478bd9Sstevel@tonic-gate 	np = kmem_zalloc(sizeof (struct nfs4_server), KM_SLEEP);
38977c478bd9Sstevel@tonic-gate 	np->saddr.len = svp->sv_addr.len;
38987c478bd9Sstevel@tonic-gate 	np->saddr.maxlen = svp->sv_addr.maxlen;
38997c478bd9Sstevel@tonic-gate 	np->saddr.buf = kmem_alloc(svp->sv_addr.maxlen, KM_SLEEP);
39007c478bd9Sstevel@tonic-gate 	bcopy(svp->sv_addr.buf, np->saddr.buf, svp->sv_addr.len);
39017c478bd9Sstevel@tonic-gate 	np->s_refcnt = 1;
39027c478bd9Sstevel@tonic-gate 
39037c478bd9Sstevel@tonic-gate 	/*
39047c478bd9Sstevel@tonic-gate 	 * Build the nfs_client_id4 for this server mount.  Ensure
39057c478bd9Sstevel@tonic-gate 	 * the verifier is useful and that the identification is
39067c478bd9Sstevel@tonic-gate 	 * somehow based on the server's address for the case of
39077c478bd9Sstevel@tonic-gate 	 * multi-homed servers.
39087c478bd9Sstevel@tonic-gate 	 */
39097c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_verifier = 0;
39107c478bd9Sstevel@tonic-gate 	gethrestime(&tt);
39117c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_curtime.sec = (uint32_t)tt.tv_sec;
39127c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_curtime.subsec = (uint32_t)tt.tv_nsec;
39137c478bd9Sstevel@tonic-gate 	np->clidtosend.verifier = nfs4clientid_verifier.un_verifier;
39147c478bd9Sstevel@tonic-gate 
39157c478bd9Sstevel@tonic-gate 	/*
39167c478bd9Sstevel@tonic-gate 	 * calculate the length of the opaque identifier.  Subtract 2
39177c478bd9Sstevel@tonic-gate 	 * for the "%s" and add the traditional +1 for null
39187c478bd9Sstevel@tonic-gate 	 * termination.
39197c478bd9Sstevel@tonic-gate 	 */
39207c478bd9Sstevel@tonic-gate 	len = strlen(id_val) - 2 + strlen(uts_nodename()) + 1;
39217c478bd9Sstevel@tonic-gate 	np->clidtosend.id_len = len + np->saddr.maxlen;
39227c478bd9Sstevel@tonic-gate 
39237c478bd9Sstevel@tonic-gate 	np->clidtosend.id_val = kmem_alloc(np->clidtosend.id_len, KM_SLEEP);
39247c478bd9Sstevel@tonic-gate 	(void) sprintf(np->clidtosend.id_val, id_val, uts_nodename());
39257c478bd9Sstevel@tonic-gate 	bcopy(np->saddr.buf, &np->clidtosend.id_val[len], np->saddr.len);
39267c478bd9Sstevel@tonic-gate 
39277c478bd9Sstevel@tonic-gate 	np->s_flags = 0;
39287c478bd9Sstevel@tonic-gate 	np->mntinfo4_list = NULL;
39297c478bd9Sstevel@tonic-gate 	/* save cred for issuing rfs4calls inside the renew thread */
39307c478bd9Sstevel@tonic-gate 	crhold(cr);
39317c478bd9Sstevel@tonic-gate 	np->s_cred = cr;
39327c478bd9Sstevel@tonic-gate 	cv_init(&np->cv_thread_exit, NULL, CV_DEFAULT, NULL);
39337c478bd9Sstevel@tonic-gate 	mutex_init(&np->s_lock, NULL, MUTEX_DEFAULT, NULL);
39347c478bd9Sstevel@tonic-gate 	nfs_rw_init(&np->s_recovlock, NULL, RW_DEFAULT, NULL);
39357c478bd9Sstevel@tonic-gate 	list_create(&np->s_deleg_list, sizeof (rnode4_t),
39367c478bd9Sstevel@tonic-gate 	    offsetof(rnode4_t, r_deleg_link));
39377c478bd9Sstevel@tonic-gate 	np->s_thread_exit = 0;
39387c478bd9Sstevel@tonic-gate 	np->state_ref_count = 0;
39397c478bd9Sstevel@tonic-gate 	np->lease_valid = NFS4_LEASE_NOT_STARTED;
39407c478bd9Sstevel@tonic-gate 	cv_init(&np->s_cv_otw_count, NULL, CV_DEFAULT, NULL);
3941f86c6ccaSdm120769 	cv_init(&np->s_clientid_pend, NULL, CV_DEFAULT, NULL);
39427c478bd9Sstevel@tonic-gate 	np->s_otw_call_count = 0;
39437c478bd9Sstevel@tonic-gate 	cv_init(&np->wait_cb_null, NULL, CV_DEFAULT, NULL);
39447c478bd9Sstevel@tonic-gate 	np->zoneid = getzoneid();
39457c478bd9Sstevel@tonic-gate 	np->zone_globals = nfs4_get_callback_globals();
39467c478bd9Sstevel@tonic-gate 	ASSERT(np->zone_globals != NULL);
39477c478bd9Sstevel@tonic-gate 	return (np);
39487c478bd9Sstevel@tonic-gate }
39497c478bd9Sstevel@tonic-gate 
39507c478bd9Sstevel@tonic-gate /*
39517c478bd9Sstevel@tonic-gate  * Create a new nfs4_server_t structure and add it to the list.
39527c478bd9Sstevel@tonic-gate  * Returns new node locked; reference must eventually be freed.
39537c478bd9Sstevel@tonic-gate  */
39547c478bd9Sstevel@tonic-gate static struct nfs4_server *
add_new_nfs4_server(struct servinfo4 * svp,cred_t * cr)39557c478bd9Sstevel@tonic-gate add_new_nfs4_server(struct servinfo4 *svp, cred_t *cr)
39567c478bd9Sstevel@tonic-gate {
39577c478bd9Sstevel@tonic-gate 	nfs4_server_t *sp;
39587c478bd9Sstevel@tonic-gate 
39597c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&nfs4_server_lst_lock));
39607c478bd9Sstevel@tonic-gate 	sp = new_nfs4_server(svp, cr);
39617c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
39627c478bd9Sstevel@tonic-gate 	insque(sp, &nfs4_server_lst);
39637c478bd9Sstevel@tonic-gate 	sp->s_refcnt++;			/* list gets a reference */
3964f64c4ae1Sdm120769 	sp->s_flags |= N4S_INSERTED;
39657c478bd9Sstevel@tonic-gate 	sp->clientid = 0;
39667c478bd9Sstevel@tonic-gate 	return (sp);
39677c478bd9Sstevel@tonic-gate }
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate int nfs4_server_t_debug = 0;
39707c478bd9Sstevel@tonic-gate 
39717c478bd9Sstevel@tonic-gate #ifdef lint
39727c478bd9Sstevel@tonic-gate extern void
39737c478bd9Sstevel@tonic-gate dumpnfs4slist(char *, mntinfo4_t *, clientid4, servinfo4_t *);
39747c478bd9Sstevel@tonic-gate #endif
39757c478bd9Sstevel@tonic-gate 
39767c478bd9Sstevel@tonic-gate #ifndef lint
39777c478bd9Sstevel@tonic-gate #ifdef DEBUG
39787c478bd9Sstevel@tonic-gate void
dumpnfs4slist(char * txt,mntinfo4_t * mi,clientid4 clientid,servinfo4_t * srv_p)39797c478bd9Sstevel@tonic-gate dumpnfs4slist(char *txt, mntinfo4_t *mi, clientid4 clientid, servinfo4_t *srv_p)
39807c478bd9Sstevel@tonic-gate {
39817c478bd9Sstevel@tonic-gate 	int hash16(void *p, int len);
39827c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
39837c478bd9Sstevel@tonic-gate 
39847c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_server_t_debug, (CE_NOTE,
39857c478bd9Sstevel@tonic-gate 	    "dumping nfs4_server_t list in %s", txt));
39867c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39877c478bd9Sstevel@tonic-gate 	    "mi 0x%p, want clientid %llx, addr %d/%04X",
39887c478bd9Sstevel@tonic-gate 	    mi, (longlong_t)clientid, srv_p->sv_addr.len,
39897c478bd9Sstevel@tonic-gate 	    hash16((void *)srv_p->sv_addr.buf, srv_p->sv_addr.len)));
39907c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst;
39917c478bd9Sstevel@tonic-gate 	    np = np->forw) {
39927c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39937c478bd9Sstevel@tonic-gate 		    "node 0x%p,    clientid %llx, addr %d/%04X, cnt %d",
39947c478bd9Sstevel@tonic-gate 		    np, (longlong_t)np->clientid, np->saddr.len,
39957c478bd9Sstevel@tonic-gate 		    hash16((void *)np->saddr.buf, np->saddr.len),
39967c478bd9Sstevel@tonic-gate 		    np->state_ref_count));
39977c478bd9Sstevel@tonic-gate 		if (np->saddr.len == srv_p->sv_addr.len &&
39987c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, srv_p->sv_addr.buf,
39997c478bd9Sstevel@tonic-gate 		    np->saddr.len) == 0)
40007c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
40017c478bd9Sstevel@tonic-gate 			    " - address matches"));
40027c478bd9Sstevel@tonic-gate 		if (np->clientid == clientid || np->clientid == 0)
40037c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
40047c478bd9Sstevel@tonic-gate 			    " - clientid matches"));
40057c478bd9Sstevel@tonic-gate 		if (np->s_thread_exit != NFS4_THREAD_EXIT)
40067c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
40077c478bd9Sstevel@tonic-gate 			    " - thread not exiting"));
40087c478bd9Sstevel@tonic-gate 	}
40097c478bd9Sstevel@tonic-gate 	delay(hz);
40107c478bd9Sstevel@tonic-gate }
40117c478bd9Sstevel@tonic-gate #endif
40127c478bd9Sstevel@tonic-gate #endif
40137c478bd9Sstevel@tonic-gate 
40147c478bd9Sstevel@tonic-gate 
40157c478bd9Sstevel@tonic-gate /*
40167c478bd9Sstevel@tonic-gate  * Move a mntinfo4_t from one server list to another.
40177c478bd9Sstevel@tonic-gate  * Locking of the two nfs4_server_t nodes will be done in list order.
40187c478bd9Sstevel@tonic-gate  *
40197c478bd9Sstevel@tonic-gate  * Returns NULL if the current nfs4_server_t for the filesystem could not
40207c478bd9Sstevel@tonic-gate  * be found (e.g., due to forced unmount).  Otherwise returns a reference
40217c478bd9Sstevel@tonic-gate  * to the new nfs4_server_t, which must eventually be freed.
40227c478bd9Sstevel@tonic-gate  */
40237c478bd9Sstevel@tonic-gate nfs4_server_t *
nfs4_move_mi(mntinfo4_t * mi,servinfo4_t * old,servinfo4_t * new)40247c478bd9Sstevel@tonic-gate nfs4_move_mi(mntinfo4_t *mi, servinfo4_t *old, servinfo4_t *new)
40257c478bd9Sstevel@tonic-gate {
40267c478bd9Sstevel@tonic-gate 	nfs4_server_t *p, *op = NULL, *np = NULL;
40277c478bd9Sstevel@tonic-gate 	int num_open;
4028108322fbScarlsonj 	zoneid_t zoneid = nfs_zoneid();
40297c478bd9Sstevel@tonic-gate 
4030108322fbScarlsonj 	ASSERT(nfs_zone() == mi->mi_zone);
40317c478bd9Sstevel@tonic-gate 
40327c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
40337c478bd9Sstevel@tonic-gate #ifdef DEBUG
40347c478bd9Sstevel@tonic-gate 	if (nfs4_server_t_debug)
40357c478bd9Sstevel@tonic-gate 		dumpnfs4slist("nfs4_move_mi", mi, (clientid4)0, new);
40367c478bd9Sstevel@tonic-gate #endif
40377c478bd9Sstevel@tonic-gate 	for (p = nfs4_server_lst.forw; p != &nfs4_server_lst; p = p->forw) {
40387c478bd9Sstevel@tonic-gate 		if (p->zoneid != zoneid)
40397c478bd9Sstevel@tonic-gate 			continue;
40407c478bd9Sstevel@tonic-gate 		if (p->saddr.len == old->sv_addr.len &&
40417c478bd9Sstevel@tonic-gate 		    bcmp(p->saddr.buf, old->sv_addr.buf, p->saddr.len) == 0 &&
40427c478bd9Sstevel@tonic-gate 		    p->s_thread_exit != NFS4_THREAD_EXIT) {
40437c478bd9Sstevel@tonic-gate 			op = p;
40447c478bd9Sstevel@tonic-gate 			mutex_enter(&op->s_lock);
40457c478bd9Sstevel@tonic-gate 			op->s_refcnt++;
40467c478bd9Sstevel@tonic-gate 		}
40477c478bd9Sstevel@tonic-gate 		if (p->saddr.len == new->sv_addr.len &&
40487c478bd9Sstevel@tonic-gate 		    bcmp(p->saddr.buf, new->sv_addr.buf, p->saddr.len) == 0 &&
40497c478bd9Sstevel@tonic-gate 		    p->s_thread_exit != NFS4_THREAD_EXIT) {
40507c478bd9Sstevel@tonic-gate 			np = p;
40517c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
40527c478bd9Sstevel@tonic-gate 		}
40537c478bd9Sstevel@tonic-gate 		if (op != NULL && np != NULL)
40547c478bd9Sstevel@tonic-gate 			break;
40557c478bd9Sstevel@tonic-gate 	}
40567c478bd9Sstevel@tonic-gate 	if (op == NULL) {
40577c478bd9Sstevel@tonic-gate 		/*
40587c478bd9Sstevel@tonic-gate 		 * Filesystem has been forcibly unmounted.  Bail out.
40597c478bd9Sstevel@tonic-gate 		 */
40607c478bd9Sstevel@tonic-gate 		if (np != NULL)
40617c478bd9Sstevel@tonic-gate 			mutex_exit(&np->s_lock);
40627c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_server_lst_lock);
40637c478bd9Sstevel@tonic-gate 		return (NULL);
40647c478bd9Sstevel@tonic-gate 	}
40657c478bd9Sstevel@tonic-gate 	if (np != NULL) {
40667c478bd9Sstevel@tonic-gate 		np->s_refcnt++;
40677c478bd9Sstevel@tonic-gate 	} else {
40687c478bd9Sstevel@tonic-gate #ifdef DEBUG
40697c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40707c478bd9Sstevel@tonic-gate 		    "nfs4_move_mi: no target nfs4_server, will create."));
40717c478bd9Sstevel@tonic-gate #endif
40727c478bd9Sstevel@tonic-gate 		np = add_new_nfs4_server(new, kcred);
40737c478bd9Sstevel@tonic-gate 	}
40747c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
40757c478bd9Sstevel@tonic-gate 
40767c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40777c478bd9Sstevel@tonic-gate 	    "nfs4_move_mi: for mi 0x%p, "
40787c478bd9Sstevel@tonic-gate 	    "old servinfo4 0x%p, new servinfo4 0x%p, "
40797c478bd9Sstevel@tonic-gate 	    "old nfs4_server 0x%p, new nfs4_server 0x%p, ",
40807c478bd9Sstevel@tonic-gate 	    (void*)mi, (void*)old, (void*)new,
40817c478bd9Sstevel@tonic-gate 	    (void*)op, (void*)np));
40827c478bd9Sstevel@tonic-gate 	ASSERT(op != NULL && np != NULL);
40837c478bd9Sstevel@tonic-gate 
40847c478bd9Sstevel@tonic-gate 	/* discard any delegations */
40857c478bd9Sstevel@tonic-gate 	nfs4_deleg_discard(mi, op);
40867c478bd9Sstevel@tonic-gate 
40877c478bd9Sstevel@tonic-gate 	num_open = mi->mi_open_files;
40887c478bd9Sstevel@tonic-gate 	mi->mi_open_files = 0;
40897c478bd9Sstevel@tonic-gate 	op->state_ref_count -= num_open;
40907c478bd9Sstevel@tonic-gate 	ASSERT(op->state_ref_count >= 0);
40917c478bd9Sstevel@tonic-gate 	np->state_ref_count += num_open;
40927c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server_nolock(mi, op);
40937c478bd9Sstevel@tonic-gate 	mi->mi_open_files = num_open;
40947c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40957c478bd9Sstevel@tonic-gate 	    "nfs4_move_mi: mi_open_files %d, op->cnt %d, np->cnt %d",
40967c478bd9Sstevel@tonic-gate 	    mi->mi_open_files, op->state_ref_count, np->state_ref_count));
40977c478bd9Sstevel@tonic-gate 
40987c478bd9Sstevel@tonic-gate 	nfs4_add_mi_to_server(np, mi);
40997c478bd9Sstevel@tonic-gate 
41007c478bd9Sstevel@tonic-gate 	mutex_exit(&op->s_lock);
41017c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
410295c7fa91SPavel Filipensky 	nfs4_server_rele(op);
41037c478bd9Sstevel@tonic-gate 
41047c478bd9Sstevel@tonic-gate 	return (np);
41057c478bd9Sstevel@tonic-gate }
41067c478bd9Sstevel@tonic-gate 
41077c478bd9Sstevel@tonic-gate /*
4108f86c6ccaSdm120769  * Need to have the nfs4_server_lst_lock.
41097c478bd9Sstevel@tonic-gate  * Search the nfs4_server list to find a match on this servinfo4
41107c478bd9Sstevel@tonic-gate  * based on its address.
41117c478bd9Sstevel@tonic-gate  *
41127c478bd9Sstevel@tonic-gate  * Returns NULL if no match is found.  Otherwise returns a reference (which
41137c478bd9Sstevel@tonic-gate  * must eventually be freed) to a locked nfs4_server.
41147c478bd9Sstevel@tonic-gate  */
41157c478bd9Sstevel@tonic-gate nfs4_server_t *
servinfo4_to_nfs4_server(servinfo4_t * srv_p)41167c478bd9Sstevel@tonic-gate servinfo4_to_nfs4_server(servinfo4_t *srv_p)
41177c478bd9Sstevel@tonic-gate {
41187c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
4119108322fbScarlsonj 	zoneid_t zoneid = nfs_zoneid();
41207c478bd9Sstevel@tonic-gate 
4121f86c6ccaSdm120769 	ASSERT(MUTEX_HELD(&nfs4_server_lst_lock));
41227c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
41237c478bd9Sstevel@tonic-gate 		if (np->zoneid == zoneid &&
41247c478bd9Sstevel@tonic-gate 		    np->saddr.len == srv_p->sv_addr.len &&
41257c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, srv_p->sv_addr.buf,
41267c478bd9Sstevel@tonic-gate 		    np->saddr.len) == 0 &&
41277c478bd9Sstevel@tonic-gate 		    np->s_thread_exit != NFS4_THREAD_EXIT) {
41287c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
41297c478bd9Sstevel@tonic-gate 			np->s_refcnt++;
41307c478bd9Sstevel@tonic-gate 			return (np);
41317c478bd9Sstevel@tonic-gate 		}
41327c478bd9Sstevel@tonic-gate 	}
41337c478bd9Sstevel@tonic-gate 	return (NULL);
41347c478bd9Sstevel@tonic-gate }
41357c478bd9Sstevel@tonic-gate 
41367c478bd9Sstevel@tonic-gate /*
41377c478bd9Sstevel@tonic-gate  * Locks the nfs4_server down if it is found and returns a reference that
41387c478bd9Sstevel@tonic-gate  * must eventually be freed.
413922dc8f51SPavel Filipensky  */
414022dc8f51SPavel Filipensky static nfs4_server_t *
lookup_nfs4_server(nfs4_server_t * sp,int any_state)414122dc8f51SPavel Filipensky lookup_nfs4_server(nfs4_server_t *sp, int any_state)
414222dc8f51SPavel Filipensky {
414322dc8f51SPavel Filipensky 	nfs4_server_t *np;
414422dc8f51SPavel Filipensky 
414522dc8f51SPavel Filipensky 	mutex_enter(&nfs4_server_lst_lock);
414622dc8f51SPavel Filipensky 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
414722dc8f51SPavel Filipensky 		mutex_enter(&np->s_lock);
414822dc8f51SPavel Filipensky 		if (np == sp && np->s_refcnt > 0 &&
414922dc8f51SPavel Filipensky 		    (np->s_thread_exit != NFS4_THREAD_EXIT || any_state)) {
415022dc8f51SPavel Filipensky 			mutex_exit(&nfs4_server_lst_lock);
415122dc8f51SPavel Filipensky 			np->s_refcnt++;
415222dc8f51SPavel Filipensky 			return (np);
415322dc8f51SPavel Filipensky 		}
415422dc8f51SPavel Filipensky 		mutex_exit(&np->s_lock);
415522dc8f51SPavel Filipensky 	}
415622dc8f51SPavel Filipensky 	mutex_exit(&nfs4_server_lst_lock);
415722dc8f51SPavel Filipensky 
415822dc8f51SPavel Filipensky 	return (NULL);
415922dc8f51SPavel Filipensky }
416022dc8f51SPavel Filipensky 
416122dc8f51SPavel Filipensky /*
41627c478bd9Sstevel@tonic-gate  * The caller should be holding mi->mi_recovlock, and it should continue to
41637c478bd9Sstevel@tonic-gate  * hold the lock until done with the returned nfs4_server_t.  Once
41647c478bd9Sstevel@tonic-gate  * mi->mi_recovlock is released, there is no guarantee that the returned
41657c478bd9Sstevel@tonic-gate  * mi->nfs4_server_t will continue to correspond to mi.
41667c478bd9Sstevel@tonic-gate  */
41677c478bd9Sstevel@tonic-gate nfs4_server_t *
find_nfs4_server(mntinfo4_t * mi)41687c478bd9Sstevel@tonic-gate find_nfs4_server(mntinfo4_t *mi)
41697c478bd9Sstevel@tonic-gate {
417022dc8f51SPavel Filipensky 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
417122dc8f51SPavel Filipensky 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
417222dc8f51SPavel Filipensky 
417322dc8f51SPavel Filipensky 	return (lookup_nfs4_server(mi->mi_srv, 0));
41747c478bd9Sstevel@tonic-gate }
41757c478bd9Sstevel@tonic-gate 
41767c478bd9Sstevel@tonic-gate /*
417722dc8f51SPavel Filipensky  * Same as above, but takes an "any_state" parameter which can be
41787c478bd9Sstevel@tonic-gate  * set to 1 if the caller wishes to find nfs4_server_t's which
41797c478bd9Sstevel@tonic-gate  * have been marked for termination by the exit of the renew
41807c478bd9Sstevel@tonic-gate  * thread.  This should only be used by operations which are
41817c478bd9Sstevel@tonic-gate  * cleaning up and will not cause an OTW op.
41827c478bd9Sstevel@tonic-gate  */
41837c478bd9Sstevel@tonic-gate nfs4_server_t *
find_nfs4_server_all(mntinfo4_t * mi,int any_state)418422dc8f51SPavel Filipensky find_nfs4_server_all(mntinfo4_t *mi, int any_state)
41857c478bd9Sstevel@tonic-gate {
41867c478bd9Sstevel@tonic-gate 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
41877c478bd9Sstevel@tonic-gate 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
41887c478bd9Sstevel@tonic-gate 
418922dc8f51SPavel Filipensky 	return (lookup_nfs4_server(mi->mi_srv, any_state));
41907c478bd9Sstevel@tonic-gate }
419122dc8f51SPavel Filipensky 
419222dc8f51SPavel Filipensky /*
419322dc8f51SPavel Filipensky  * Lock sp, but only if it's still active (in the list and hasn't been
419422dc8f51SPavel Filipensky  * flagged as exiting) or 'any_state' is non-zero.
419522dc8f51SPavel Filipensky  * Returns TRUE if sp got locked and adds a reference to sp.
419622dc8f51SPavel Filipensky  */
419722dc8f51SPavel Filipensky bool_t
nfs4_server_vlock(nfs4_server_t * sp,int any_state)419822dc8f51SPavel Filipensky nfs4_server_vlock(nfs4_server_t *sp, int any_state)
419922dc8f51SPavel Filipensky {
420022dc8f51SPavel Filipensky 	return (lookup_nfs4_server(sp, any_state) != NULL);
42017c478bd9Sstevel@tonic-gate }
42027c478bd9Sstevel@tonic-gate 
42037c478bd9Sstevel@tonic-gate /*
42047c478bd9Sstevel@tonic-gate  * Release the reference to sp and destroy it if that's the last one.
42057c478bd9Sstevel@tonic-gate  */
42067c478bd9Sstevel@tonic-gate 
42077c478bd9Sstevel@tonic-gate void
nfs4_server_rele(nfs4_server_t * sp)42087c478bd9Sstevel@tonic-gate nfs4_server_rele(nfs4_server_t *sp)
42097c478bd9Sstevel@tonic-gate {
42107c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
42117c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_refcnt > 0);
42127c478bd9Sstevel@tonic-gate 	sp->s_refcnt--;
42137c478bd9Sstevel@tonic-gate 	if (sp->s_refcnt > 0) {
42147c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
42157c478bd9Sstevel@tonic-gate 		return;
42167c478bd9Sstevel@tonic-gate 	}
42177c478bd9Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
4218f86c6ccaSdm120769 
42197c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
42207c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
42217c478bd9Sstevel@tonic-gate 	if (sp->s_refcnt > 0) {
42227c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
42237c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_server_lst_lock);
42247c478bd9Sstevel@tonic-gate 		return;
42257c478bd9Sstevel@tonic-gate 	}
42267c478bd9Sstevel@tonic-gate 	remque(sp);
42277c478bd9Sstevel@tonic-gate 	sp->forw = sp->back = NULL;
42287c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
42297c478bd9Sstevel@tonic-gate 	destroy_nfs4_server(sp);
42307c478bd9Sstevel@tonic-gate }
42317c478bd9Sstevel@tonic-gate 
42327c478bd9Sstevel@tonic-gate static void
destroy_nfs4_server(nfs4_server_t * sp)42337c478bd9Sstevel@tonic-gate destroy_nfs4_server(nfs4_server_t *sp)
42347c478bd9Sstevel@tonic-gate {
42357c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
42367c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_refcnt == 0);
42377c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_otw_call_count == 0);
42387c478bd9Sstevel@tonic-gate 
42397c478bd9Sstevel@tonic-gate 	remove_all_mi(sp);
42407c478bd9Sstevel@tonic-gate 
42417c478bd9Sstevel@tonic-gate 	crfree(sp->s_cred);
42427c478bd9Sstevel@tonic-gate 	kmem_free(sp->saddr.buf, sp->saddr.maxlen);
42437c478bd9Sstevel@tonic-gate 	kmem_free(sp->clidtosend.id_val, sp->clidtosend.id_len);
42447c478bd9Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
42457c478bd9Sstevel@tonic-gate 
42467c478bd9Sstevel@tonic-gate 	/* destroy the nfs4_server */
42477c478bd9Sstevel@tonic-gate 	nfs4callback_destroy(sp);
42487c478bd9Sstevel@tonic-gate 	list_destroy(&sp->s_deleg_list);
42497c478bd9Sstevel@tonic-gate 	mutex_destroy(&sp->s_lock);
42507c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->cv_thread_exit);
42517c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->s_cv_otw_count);
4252f86c6ccaSdm120769 	cv_destroy(&sp->s_clientid_pend);
42537c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->wait_cb_null);
42547c478bd9Sstevel@tonic-gate 	nfs_rw_destroy(&sp->s_recovlock);
42557c478bd9Sstevel@tonic-gate 	kmem_free(sp, sizeof (*sp));
42567c478bd9Sstevel@tonic-gate }
42577c478bd9Sstevel@tonic-gate 
42587c478bd9Sstevel@tonic-gate /*
42597c478bd9Sstevel@tonic-gate  * Fork off a thread to free the data structures for a mount.
42607c478bd9Sstevel@tonic-gate  */
42617c478bd9Sstevel@tonic-gate 
42627c478bd9Sstevel@tonic-gate static void
async_free_mount(vfs_t * vfsp,int flag,cred_t * cr)4263b9238976Sth199096 async_free_mount(vfs_t *vfsp, int flag, cred_t *cr)
42647c478bd9Sstevel@tonic-gate {
42657c478bd9Sstevel@tonic-gate 	freemountargs_t *args;
42667c478bd9Sstevel@tonic-gate 	args = kmem_alloc(sizeof (freemountargs_t), KM_SLEEP);
42677c478bd9Sstevel@tonic-gate 	args->fm_vfsp = vfsp;
42687c478bd9Sstevel@tonic-gate 	VFS_HOLD(vfsp);
426950a83466Sjwahlig 	MI4_HOLD(VFTOMI4(vfsp));
4270b9238976Sth199096 	args->fm_flag = flag;
42717c478bd9Sstevel@tonic-gate 	args->fm_cr = cr;
42727c478bd9Sstevel@tonic-gate 	crhold(cr);
42737c478bd9Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, nfs4_free_mount_thread, args, 0,
42747c478bd9Sstevel@tonic-gate 	    minclsyspri);
42757c478bd9Sstevel@tonic-gate }
42767c478bd9Sstevel@tonic-gate 
42777c478bd9Sstevel@tonic-gate static void
nfs4_free_mount_thread(freemountargs_t * args)42787c478bd9Sstevel@tonic-gate nfs4_free_mount_thread(freemountargs_t *args)
42797c478bd9Sstevel@tonic-gate {
428050a83466Sjwahlig 	mntinfo4_t *mi;
4281b9238976Sth199096 	nfs4_free_mount(args->fm_vfsp, args->fm_flag, args->fm_cr);
428250a83466Sjwahlig 	mi = VFTOMI4(args->fm_vfsp);
42837c478bd9Sstevel@tonic-gate 	crfree(args->fm_cr);
428450a83466Sjwahlig 	VFS_RELE(args->fm_vfsp);
428550a83466Sjwahlig 	MI4_RELE(mi);
42867c478bd9Sstevel@tonic-gate 	kmem_free(args, sizeof (freemountargs_t));
42877c478bd9Sstevel@tonic-gate 	zthread_exit();
42887c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
42897c478bd9Sstevel@tonic-gate }
42907c478bd9Sstevel@tonic-gate 
42917c478bd9Sstevel@tonic-gate /*
42927c478bd9Sstevel@tonic-gate  * Thread to free the data structures for a given filesystem.
42937c478bd9Sstevel@tonic-gate  */
42947c478bd9Sstevel@tonic-gate static void
nfs4_free_mount(vfs_t * vfsp,int flag,cred_t * cr)4295b9238976Sth199096 nfs4_free_mount(vfs_t *vfsp, int flag, cred_t *cr)
42967c478bd9Sstevel@tonic-gate {
42977c478bd9Sstevel@tonic-gate 	mntinfo4_t		*mi = VFTOMI4(vfsp);
42987c478bd9Sstevel@tonic-gate 	nfs4_server_t		*sp;
42997c478bd9Sstevel@tonic-gate 	callb_cpr_t		cpr_info;
43007c478bd9Sstevel@tonic-gate 	kmutex_t		cpr_lock;
43017c478bd9Sstevel@tonic-gate 	boolean_t		async_thread;
430250a83466Sjwahlig 	int			removed;
43037c478bd9Sstevel@tonic-gate 
4304d3a14591SThomas Haynes 	bool_t			must_unlock;
4305b9238976Sth199096 	nfs4_ephemeral_tree_t	*eph_tree;
4306b9238976Sth199096 
43077c478bd9Sstevel@tonic-gate 	/*
43087c478bd9Sstevel@tonic-gate 	 * We need to participate in the CPR framework if this is a kernel
43097c478bd9Sstevel@tonic-gate 	 * thread.
43107c478bd9Sstevel@tonic-gate 	 */
4311108322fbScarlsonj 	async_thread = (curproc == nfs_zone()->zone_zsched);
43127c478bd9Sstevel@tonic-gate 	if (async_thread) {
43137c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL);
43147c478bd9Sstevel@tonic-gate 		CALLB_CPR_INIT(&cpr_info, &cpr_lock, callb_generic_cpr,
43157c478bd9Sstevel@tonic-gate 		    "nfsv4AsyncUnmount");
43167c478bd9Sstevel@tonic-gate 	}
43177c478bd9Sstevel@tonic-gate 
43187c478bd9Sstevel@tonic-gate 	/*
43197c478bd9Sstevel@tonic-gate 	 * We need to wait for all outstanding OTW calls
43207c478bd9Sstevel@tonic-gate 	 * and recovery to finish before we remove the mi
43217c478bd9Sstevel@tonic-gate 	 * from the nfs4_server_t, as current pending
43227c478bd9Sstevel@tonic-gate 	 * calls might still need this linkage (in order
43237c478bd9Sstevel@tonic-gate 	 * to find a nfs4_server_t from a mntinfo4_t).
43247c478bd9Sstevel@tonic-gate 	 */
43257c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, FALSE);
43267c478bd9Sstevel@tonic-gate 	sp = find_nfs4_server(mi);
43277c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&mi->mi_recovlock);
43287c478bd9Sstevel@tonic-gate 
43297c478bd9Sstevel@tonic-gate 	if (sp) {
43307c478bd9Sstevel@tonic-gate 		while (sp->s_otw_call_count != 0) {
43317c478bd9Sstevel@tonic-gate 			if (async_thread) {
43327c478bd9Sstevel@tonic-gate 				mutex_enter(&cpr_lock);
43337c478bd9Sstevel@tonic-gate 				CALLB_CPR_SAFE_BEGIN(&cpr_info);
43347c478bd9Sstevel@tonic-gate 				mutex_exit(&cpr_lock);
43357c478bd9Sstevel@tonic-gate 			}
43367c478bd9Sstevel@tonic-gate 			cv_wait(&sp->s_cv_otw_count, &sp->s_lock);
43377c478bd9Sstevel@tonic-gate 			if (async_thread) {
43387c478bd9Sstevel@tonic-gate 				mutex_enter(&cpr_lock);
43397c478bd9Sstevel@tonic-gate 				CALLB_CPR_SAFE_END(&cpr_info, &cpr_lock);
43407c478bd9Sstevel@tonic-gate 				mutex_exit(&cpr_lock);
43417c478bd9Sstevel@tonic-gate 			}
43427c478bd9Sstevel@tonic-gate 		}
43437c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
43447c478bd9Sstevel@tonic-gate 		nfs4_server_rele(sp);
43457c478bd9Sstevel@tonic-gate 		sp = NULL;
43467c478bd9Sstevel@tonic-gate 	}
43477c478bd9Sstevel@tonic-gate 
43487c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
43497c478bd9Sstevel@tonic-gate 	while (mi->mi_in_recovery != 0) {
43507c478bd9Sstevel@tonic-gate 		if (async_thread) {
43517c478bd9Sstevel@tonic-gate 			mutex_enter(&cpr_lock);
43527c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_BEGIN(&cpr_info);
43537c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_lock);
43547c478bd9Sstevel@tonic-gate 		}
43557c478bd9Sstevel@tonic-gate 		cv_wait(&mi->mi_cv_in_recov, &mi->mi_lock);
43567c478bd9Sstevel@tonic-gate 		if (async_thread) {
43577c478bd9Sstevel@tonic-gate 			mutex_enter(&cpr_lock);
43587c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cpr_info, &cpr_lock);
43597c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_lock);
43607c478bd9Sstevel@tonic-gate 		}
43617c478bd9Sstevel@tonic-gate 	}
43627c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
43637c478bd9Sstevel@tonic-gate 
4364eabd0450Sth199096 	/*
4365eabd0450Sth199096 	 * If we got an error, then do not nuke the
4366eabd0450Sth199096 	 * tree. Either the harvester is busy reclaiming
4367eabd0450Sth199096 	 * this node or we ran into some busy condition.
4368eabd0450Sth199096 	 *
4369eabd0450Sth199096 	 * The harvester will eventually come along and cleanup.
4370eabd0450Sth199096 	 * The only problem would be the root mount point.
4371eabd0450Sth199096 	 *
4372eabd0450Sth199096 	 * Since the busy node can occur for a variety
4373eabd0450Sth199096 	 * of reasons and can result in an entry staying
4374eabd0450Sth199096 	 * in df output but no longer accessible from the
4375eabd0450Sth199096 	 * directory tree, we are okay.
4376eabd0450Sth199096 	 */
4377eabd0450Sth199096 	if (!nfs4_ephemeral_umount(mi, flag, cr,
43782f172c55SRobert Thurlow 	    &must_unlock, &eph_tree))
4379eabd0450Sth199096 		nfs4_ephemeral_umount_activate(mi, &must_unlock,
43802f172c55SRobert Thurlow 		    &eph_tree);
4381b9238976Sth199096 
43827c478bd9Sstevel@tonic-gate 	/*
43837c478bd9Sstevel@tonic-gate 	 * The original purge of the dnlc via 'dounmount'
43847c478bd9Sstevel@tonic-gate 	 * doesn't guarantee that another dnlc entry was not
43857c478bd9Sstevel@tonic-gate 	 * added while we waitied for all outstanding OTW
43867c478bd9Sstevel@tonic-gate 	 * and recovery calls to finish.  So re-purge the
43877c478bd9Sstevel@tonic-gate 	 * dnlc now.
43887c478bd9Sstevel@tonic-gate 	 */
43897c478bd9Sstevel@tonic-gate 	(void) dnlc_purge_vfsp(vfsp, 0);
43907c478bd9Sstevel@tonic-gate 
43917c478bd9Sstevel@tonic-gate 	/*
43927c478bd9Sstevel@tonic-gate 	 * We need to explicitly stop the manager thread; the asyc worker
43937c478bd9Sstevel@tonic-gate 	 * threads can timeout and exit on their own.
43947c478bd9Sstevel@tonic-gate 	 */
439550a83466Sjwahlig 	mutex_enter(&mi->mi_async_lock);
439650a83466Sjwahlig 	mi->mi_max_threads = 0;
43970776f5e6SVallish Vaidyeshwara 	NFS4_WAKEALL_ASYNC_WORKERS(mi->mi_async_work_cv);
439850a83466Sjwahlig 	mutex_exit(&mi->mi_async_lock);
439950a83466Sjwahlig 	if (mi->mi_manager_thread)
44007c478bd9Sstevel@tonic-gate 		nfs4_async_manager_stop(vfsp);
44017c478bd9Sstevel@tonic-gate 
44027c478bd9Sstevel@tonic-gate 	destroy_rtable4(vfsp, cr);
44037c478bd9Sstevel@tonic-gate 
44047c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server(mi, NULL);
44057c478bd9Sstevel@tonic-gate 
44067c478bd9Sstevel@tonic-gate 	if (async_thread) {
44077c478bd9Sstevel@tonic-gate 		mutex_enter(&cpr_lock);
44087c478bd9Sstevel@tonic-gate 		CALLB_CPR_EXIT(&cpr_info);	/* drops cpr_lock */
44097c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_lock);
44107c478bd9Sstevel@tonic-gate 	}
441150a83466Sjwahlig 
441250a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
441350a83466Sjwahlig 	if (removed)
4414a19609f8Sjv227347 		zone_rele_ref(&mi->mi_zone_ref, ZONE_REF_NFSV4);
44157c478bd9Sstevel@tonic-gate }
44162f172c55SRobert Thurlow 
44172f172c55SRobert Thurlow /* Referral related sub-routines */
44182f172c55SRobert Thurlow 
44192f172c55SRobert Thurlow /* Freeup knetconfig */
44202f172c55SRobert Thurlow static void
free_knconf_contents(struct knetconfig * k)44212f172c55SRobert Thurlow free_knconf_contents(struct knetconfig *k)
44222f172c55SRobert Thurlow {
44232f172c55SRobert Thurlow 	if (k == NULL)
44242f172c55SRobert Thurlow 		return;
44252f172c55SRobert Thurlow 	if (k->knc_protofmly)
44262f172c55SRobert Thurlow 		kmem_free(k->knc_protofmly, KNC_STRSIZE);
44272f172c55SRobert Thurlow 	if (k->knc_proto)
44282f172c55SRobert Thurlow 		kmem_free(k->knc_proto, KNC_STRSIZE);
44292f172c55SRobert Thurlow }
44302f172c55SRobert Thurlow 
44312f172c55SRobert Thurlow /*
44322f172c55SRobert Thurlow  * This updates newpath variable with exact name component from the
44332f172c55SRobert Thurlow  * path which gave us a NFS4ERR_MOVED error.
44342f172c55SRobert Thurlow  * If the path is /rp/aaa/bbb and nth value is 1, aaa is returned.
44352f172c55SRobert Thurlow  */
44362f172c55SRobert Thurlow static char *
extract_referral_point(const char * svp,int nth)44372f172c55SRobert Thurlow extract_referral_point(const char *svp, int nth)
44382f172c55SRobert Thurlow {
44392f172c55SRobert Thurlow 	int num_slashes = 0;
44402f172c55SRobert Thurlow 	const char *p;
44412f172c55SRobert Thurlow 	char *newpath = NULL;
44422f172c55SRobert Thurlow 	int i = 0;
44432f172c55SRobert Thurlow 
44442f172c55SRobert Thurlow 	newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
44452f172c55SRobert Thurlow 	for (p = svp; *p; p++) {
44462f172c55SRobert Thurlow 		if (*p == '/')
44472f172c55SRobert Thurlow 			num_slashes++;
44482f172c55SRobert Thurlow 		if (num_slashes == nth + 1) {
44492f172c55SRobert Thurlow 			p++;
44502f172c55SRobert Thurlow 			while (*p != '/') {
44512f172c55SRobert Thurlow 				if (*p == '\0')
44522f172c55SRobert Thurlow 					break;
44532f172c55SRobert Thurlow 				newpath[i] = *p;
44542f172c55SRobert Thurlow 				i++;
44552f172c55SRobert Thurlow 				p++;
44562f172c55SRobert Thurlow 			}
44572f172c55SRobert Thurlow 			newpath[i++] = '\0';
44582f172c55SRobert Thurlow 			break;
44592f172c55SRobert Thurlow 		}
44602f172c55SRobert Thurlow 	}
44612f172c55SRobert Thurlow 	return (newpath);
44622f172c55SRobert Thurlow }
44632f172c55SRobert Thurlow 
44642f172c55SRobert Thurlow /*
44652f172c55SRobert Thurlow  * This sets up a new path in sv_path to do a lookup of the referral point.
44662f172c55SRobert Thurlow  * If the path is /rp/aaa/bbb and the referral point is aaa,
44672f172c55SRobert Thurlow  * this updates /rp/aaa. This path will be used to get referral
44682f172c55SRobert Thurlow  * location.
44692f172c55SRobert Thurlow  */
44702f172c55SRobert Thurlow static void
setup_newsvpath(servinfo4_t * svp,int nth)44712f172c55SRobert Thurlow setup_newsvpath(servinfo4_t *svp, int nth)
44722f172c55SRobert Thurlow {
44732f172c55SRobert Thurlow 	int num_slashes = 0, pathlen, i = 0;
44742f172c55SRobert Thurlow 	char *newpath, *p;
44752f172c55SRobert Thurlow 
44762f172c55SRobert Thurlow 	newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
44772f172c55SRobert Thurlow 	for (p = svp->sv_path; *p; p++) {
44782f172c55SRobert Thurlow 		newpath[i] =  *p;
44792f172c55SRobert Thurlow 		if (*p == '/')
44802f172c55SRobert Thurlow 			num_slashes++;
44812f172c55SRobert Thurlow 		if (num_slashes == nth + 1) {
44822f172c55SRobert Thurlow 			newpath[i] = '\0';
44832f172c55SRobert Thurlow 			pathlen = strlen(newpath) + 1;
44842f172c55SRobert Thurlow 			kmem_free(svp->sv_path, svp->sv_pathlen);
44852f172c55SRobert Thurlow 			svp->sv_path = kmem_alloc(pathlen, KM_SLEEP);
44862f172c55SRobert Thurlow 			svp->sv_pathlen = pathlen;
44872f172c55SRobert Thurlow 			bcopy(newpath, svp->sv_path, pathlen);
44882f172c55SRobert Thurlow 			break;
44892f172c55SRobert Thurlow 		}
44902f172c55SRobert Thurlow 		i++;
44912f172c55SRobert Thurlow 	}
44922f172c55SRobert Thurlow 	kmem_free(newpath, MAXPATHLEN);
44932f172c55SRobert Thurlow }
4494