xref: /onnv-gate/usr/src/uts/common/fs/nfs/nfs4_callback.c (revision 11291:80bdcd03e626)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51649Sdm120769  * Common Development and Distribution License (the "License").
61649Sdm120769  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
229858SPavel.Filipensky@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
270Sstevel@tonic-gate /* All Rights Reserved */
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/param.h>
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/systm.h>
320Sstevel@tonic-gate #include <sys/cred.h>
330Sstevel@tonic-gate #include <sys/vfs.h>
340Sstevel@tonic-gate #include <sys/vnode.h>
350Sstevel@tonic-gate #include <sys/pathname.h>
360Sstevel@tonic-gate #include <sys/sysmacros.h>
370Sstevel@tonic-gate #include <sys/kmem.h>
380Sstevel@tonic-gate #include <sys/kstat.h>
390Sstevel@tonic-gate #include <sys/mkdev.h>
400Sstevel@tonic-gate #include <sys/mount.h>
410Sstevel@tonic-gate #include <sys/statvfs.h>
420Sstevel@tonic-gate #include <sys/errno.h>
430Sstevel@tonic-gate #include <sys/debug.h>
440Sstevel@tonic-gate #include <sys/cmn_err.h>
450Sstevel@tonic-gate #include <sys/utsname.h>
460Sstevel@tonic-gate #include <sys/bootconf.h>
470Sstevel@tonic-gate #include <sys/modctl.h>
480Sstevel@tonic-gate #include <sys/acl.h>
490Sstevel@tonic-gate #include <sys/flock.h>
500Sstevel@tonic-gate #include <sys/kstr.h>
510Sstevel@tonic-gate #include <sys/stropts.h>
520Sstevel@tonic-gate #include <sys/strsubr.h>
530Sstevel@tonic-gate #include <sys/atomic.h>
540Sstevel@tonic-gate #include <sys/disp.h>
550Sstevel@tonic-gate #include <sys/policy.h>
560Sstevel@tonic-gate #include <sys/list.h>
570Sstevel@tonic-gate #include <sys/zone.h>
580Sstevel@tonic-gate 
590Sstevel@tonic-gate #include <rpc/types.h>
600Sstevel@tonic-gate #include <rpc/auth.h>
610Sstevel@tonic-gate #include <rpc/rpcsec_gss.h>
620Sstevel@tonic-gate #include <rpc/clnt.h>
630Sstevel@tonic-gate #include <rpc/xdr.h>
640Sstevel@tonic-gate 
650Sstevel@tonic-gate #include <nfs/nfs.h>
660Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
670Sstevel@tonic-gate #include <nfs/mount.h>
680Sstevel@tonic-gate #include <nfs/nfs_acl.h>
690Sstevel@tonic-gate 
700Sstevel@tonic-gate #include <fs/fs_subr.h>
710Sstevel@tonic-gate 
720Sstevel@tonic-gate #include <nfs/nfs4.h>
730Sstevel@tonic-gate #include <nfs/rnode4.h>
740Sstevel@tonic-gate #include <nfs/nfs4_clnt.h>
750Sstevel@tonic-gate #include <nfs/nfssys.h>
760Sstevel@tonic-gate 
770Sstevel@tonic-gate #ifdef	DEBUG
780Sstevel@tonic-gate /*
790Sstevel@tonic-gate  * These are "special" state IDs and file handles that
800Sstevel@tonic-gate  * match any delegation state ID or file handled.  This
810Sstevel@tonic-gate  * is for testing purposes only.
820Sstevel@tonic-gate  */
830Sstevel@tonic-gate 
840Sstevel@tonic-gate stateid4 nfs4_deleg_any = { 0x7FFFFFF0 };
850Sstevel@tonic-gate char nfs4_deleg_fh[] = "\0377\0376\0375\0374";
860Sstevel@tonic-gate nfs_fh4 nfs4_deleg_anyfh = { sizeof (nfs4_deleg_fh)-1, nfs4_deleg_fh };
870Sstevel@tonic-gate nfsstat4 cb4_getattr_fail = NFS4_OK;
880Sstevel@tonic-gate nfsstat4 cb4_recall_fail = NFS4_OK;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate int nfs4_callback_debug;
910Sstevel@tonic-gate int nfs4_recall_debug;
920Sstevel@tonic-gate int nfs4_drat_debug;
930Sstevel@tonic-gate 
940Sstevel@tonic-gate #endif
950Sstevel@tonic-gate 
960Sstevel@tonic-gate #define	CB_NOTE(x)	NFS4_DEBUG(nfs4_callback_debug, (CE_NOTE, x))
970Sstevel@tonic-gate #define	CB_WARN(x)	NFS4_DEBUG(nfs4_callback_debug, (CE_WARN, x))
980Sstevel@tonic-gate #define	CB_WARN1(x, y)	NFS4_DEBUG(nfs4_callback_debug, (CE_WARN, x, y))
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate enum nfs4_delegreturn_policy nfs4_delegreturn_policy = INACTIVE;
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate static zone_key_t nfs4_callback_zone_key;
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate /*
1050Sstevel@tonic-gate  * NFS4_MAPSIZE is the number of bytes we are willing to consume
1060Sstevel@tonic-gate  * for the block allocation map when the server grants a NFS_LIMIT_BLOCK
1070Sstevel@tonic-gate  * style delegation.
1080Sstevel@tonic-gate  */
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate #define	NFS4_MAPSIZE	8192
1110Sstevel@tonic-gate #define	NFS4_MAPWORDS	NFS4_MAPSIZE/sizeof (uint_t)
1120Sstevel@tonic-gate #define	NbPW		(NBBY*sizeof (uint_t))
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate static int nfs4_num_prognums = 1024;
1150Sstevel@tonic-gate static SVC_CALLOUT_TABLE nfs4_cb_sct;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate struct nfs4_dnode {
1180Sstevel@tonic-gate 	list_node_t	linkage;
1190Sstevel@tonic-gate 	rnode4_t	*rnodep;
1200Sstevel@tonic-gate 	int		flags;		/* Flags for nfs4delegreturn_impl() */
1210Sstevel@tonic-gate };
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate static const struct nfs4_callback_stats nfs4_callback_stats_tmpl = {
1240Sstevel@tonic-gate 	{ "delegations",	KSTAT_DATA_UINT64 },
1250Sstevel@tonic-gate 	{ "cb_getattr",		KSTAT_DATA_UINT64 },
1260Sstevel@tonic-gate 	{ "cb_recall",		KSTAT_DATA_UINT64 },
1270Sstevel@tonic-gate 	{ "cb_null",		KSTAT_DATA_UINT64 },
1280Sstevel@tonic-gate 	{ "cb_dispatch",	KSTAT_DATA_UINT64 },
1290Sstevel@tonic-gate 	{ "delegaccept_r",	KSTAT_DATA_UINT64 },
1300Sstevel@tonic-gate 	{ "delegaccept_rw",	KSTAT_DATA_UINT64 },
1310Sstevel@tonic-gate 	{ "delegreturn",	KSTAT_DATA_UINT64 },
1320Sstevel@tonic-gate 	{ "callbacks",		KSTAT_DATA_UINT64 },
1330Sstevel@tonic-gate 	{ "claim_cur",		KSTAT_DATA_UINT64 },
1340Sstevel@tonic-gate 	{ "claim_cur_ok",	KSTAT_DATA_UINT64 },
1350Sstevel@tonic-gate 	{ "recall_trunc",	KSTAT_DATA_UINT64 },
1360Sstevel@tonic-gate 	{ "recall_failed",	KSTAT_DATA_UINT64 },
1370Sstevel@tonic-gate 	{ "return_limit_write",	KSTAT_DATA_UINT64 },
1380Sstevel@tonic-gate 	{ "return_limit_addmap", KSTAT_DATA_UINT64 },
1390Sstevel@tonic-gate 	{ "deleg_recover",	KSTAT_DATA_UINT64 },
1400Sstevel@tonic-gate 	{ "cb_illegal",		KSTAT_DATA_UINT64 }
1410Sstevel@tonic-gate };
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate struct nfs4_cb_port {
1440Sstevel@tonic-gate 	list_node_t		linkage; /* linkage into per-zone port list */
1450Sstevel@tonic-gate 	char			netid[KNC_STRSIZE];
1460Sstevel@tonic-gate 	char			uaddr[KNC_STRSIZE];
1470Sstevel@tonic-gate 	char			protofmly[KNC_STRSIZE];
1480Sstevel@tonic-gate 	char			proto[KNC_STRSIZE];
1490Sstevel@tonic-gate };
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate static int cb_getattr_bytes;
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate struct cb_recall_pass {
1540Sstevel@tonic-gate 	rnode4_t	*rp;
1550Sstevel@tonic-gate 	int		flags;		/* Flags for nfs4delegreturn_impl() */
1560Sstevel@tonic-gate 	bool_t		truncate;
1570Sstevel@tonic-gate };
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate static nfs4_open_stream_t *get_next_deleg_stream(rnode4_t *, int);
1600Sstevel@tonic-gate static void nfs4delegreturn_thread(struct cb_recall_pass *);
1610Sstevel@tonic-gate static int deleg_reopen(vnode_t *, bool_t *, struct nfs4_callback_globals *,
1620Sstevel@tonic-gate     int);
1630Sstevel@tonic-gate static void nfs4_dlistadd(rnode4_t *, struct nfs4_callback_globals *, int);
1640Sstevel@tonic-gate static void nfs4_dlistclean_impl(struct nfs4_callback_globals *, int);
1650Sstevel@tonic-gate static int nfs4delegreturn_impl(rnode4_t *, int,
1660Sstevel@tonic-gate     struct nfs4_callback_globals *);
1670Sstevel@tonic-gate static void nfs4delegreturn_cleanup_impl(rnode4_t *, nfs4_server_t *,
1680Sstevel@tonic-gate     struct nfs4_callback_globals *);
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate static void
cb_getattr(nfs_cb_argop4 * argop,nfs_cb_resop4 * resop,struct svc_req * req,struct compound_state * cs,struct nfs4_callback_globals * ncg)1710Sstevel@tonic-gate cb_getattr(nfs_cb_argop4 *argop, nfs_cb_resop4 *resop, struct svc_req *req,
1720Sstevel@tonic-gate 	struct compound_state *cs, struct nfs4_callback_globals *ncg)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate 	CB_GETATTR4args *args = &argop->nfs_cb_argop4_u.opcbgetattr;
1750Sstevel@tonic-gate 	CB_GETATTR4res *resp = &resop->nfs_cb_resop4_u.opcbgetattr;
1760Sstevel@tonic-gate 	rnode4_t *rp;
1770Sstevel@tonic-gate 	vnode_t *vp;
1780Sstevel@tonic-gate 	bool_t found = FALSE;
1790Sstevel@tonic-gate 	struct nfs4_server *sp;
1800Sstevel@tonic-gate 	struct fattr4 *fap;
1811232Srobinson 	rpc_inline_t *fdata;
1820Sstevel@tonic-gate 	long mapcnt;
1830Sstevel@tonic-gate 	fattr4_change change;
1840Sstevel@tonic-gate 	fattr4_size size;
1850Sstevel@tonic-gate 	uint_t rflag;
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	ncg->nfs4_callback_stats.cb_getattr.value.ui64++;
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate #ifdef DEBUG
1900Sstevel@tonic-gate 	/*
1910Sstevel@tonic-gate 	 * error injection hook: set cb_getattr_fail global to
1920Sstevel@tonic-gate 	 * NFS4 pcol error to be returned
1930Sstevel@tonic-gate 	 */
1940Sstevel@tonic-gate 	if (cb4_getattr_fail != NFS4_OK) {
1950Sstevel@tonic-gate 		*cs->statusp = resp->status = cb4_getattr_fail;
1960Sstevel@tonic-gate 		return;
1970Sstevel@tonic-gate 	}
1980Sstevel@tonic-gate #endif
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	resp->obj_attributes.attrmask = 0;
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_cb_lock);
2030Sstevel@tonic-gate 	sp = ncg->nfs4prog2server[req->rq_prog - NFS4_CALLBACK];
2040Sstevel@tonic-gate 	mutex_exit(&ncg->nfs4_cb_lock);
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	if (nfs4_server_vlock(sp, 0) == FALSE) {
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 		CB_WARN("cb_getattr: cannot find server\n");
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BADHANDLE;
2110Sstevel@tonic-gate 		return;
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	/*
2150Sstevel@tonic-gate 	 * In cb_compound, callback_ident was validated against rq_prog,
2160Sstevel@tonic-gate 	 * but we couldn't verify that it was set to the value we provided
2170Sstevel@tonic-gate 	 * at setclientid time (because we didn't have server struct yet).
2180Sstevel@tonic-gate 	 * Now we have the server struct, but don't have callback_ident
2190Sstevel@tonic-gate 	 * handy.  So, validate server struct program number against req
2200Sstevel@tonic-gate 	 * RPC's prog number.  At this point, we know the RPC prog num
2210Sstevel@tonic-gate 	 * is valid (else we wouldn't be here); however, we don't know
2220Sstevel@tonic-gate 	 * that it was the prog number we supplied to this server at
2230Sstevel@tonic-gate 	 * setclientid time.  If the prog numbers aren't equivalent, then
2240Sstevel@tonic-gate 	 * log the problem and fail the request because either cbserv
2250Sstevel@tonic-gate 	 * and/or cbclient are confused.  This will probably never happen.
2260Sstevel@tonic-gate 	 */
2270Sstevel@tonic-gate 	if (sp->s_program != req->rq_prog) {
2280Sstevel@tonic-gate #ifdef DEBUG
2290Sstevel@tonic-gate 		zcmn_err(getzoneid(), CE_WARN,
2300Sstevel@tonic-gate 		    "cb_getattr: wrong server program number srv=%d req=%d\n",
2310Sstevel@tonic-gate 		    sp->s_program, req->rq_prog);
2320Sstevel@tonic-gate #else
2330Sstevel@tonic-gate 		zcmn_err(getzoneid(), CE_WARN,
2340Sstevel@tonic-gate 		    "cb_getattr: wrong server program number\n");
2350Sstevel@tonic-gate #endif
2360Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
2370Sstevel@tonic-gate 		nfs4_server_rele(sp);
2380Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BADHANDLE;
2390Sstevel@tonic-gate 		return;
2400Sstevel@tonic-gate 	}
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	/*
2430Sstevel@tonic-gate 	 * Search the delegation list for a matching file handle;
2440Sstevel@tonic-gate 	 * mutex on sp prevents the list from changing.
2450Sstevel@tonic-gate 	 */
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	rp = list_head(&sp->s_deleg_list);
2480Sstevel@tonic-gate 	for (; rp != NULL; rp = list_next(&sp->s_deleg_list, rp)) {
2490Sstevel@tonic-gate 		nfs4_fhandle_t fhandle;
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 		sfh4_copyval(rp->r_fh, &fhandle);
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 		if ((fhandle.fh_len == args->fh.nfs_fh4_len &&
2540Sstevel@tonic-gate 		    bcmp(fhandle.fh_buf, args->fh.nfs_fh4_val,
2550Sstevel@tonic-gate 		    fhandle.fh_len) == 0)) {
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 			found = TRUE;
2580Sstevel@tonic-gate 			break;
2590Sstevel@tonic-gate 		}
2600Sstevel@tonic-gate #ifdef	DEBUG
2610Sstevel@tonic-gate 		if (nfs4_deleg_anyfh.nfs_fh4_len == args->fh.nfs_fh4_len &&
2620Sstevel@tonic-gate 		    bcmp(nfs4_deleg_anyfh.nfs_fh4_val, args->fh.nfs_fh4_val,
2630Sstevel@tonic-gate 		    args->fh.nfs_fh4_len) == 0) {
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 			found = TRUE;
2660Sstevel@tonic-gate 			break;
2670Sstevel@tonic-gate 		}
2680Sstevel@tonic-gate #endif
2690Sstevel@tonic-gate 	}
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	/*
2720Sstevel@tonic-gate 	 * VN_HOLD the vnode before releasing s_lock to guarantee
2730Sstevel@tonic-gate 	 * we have a valid vnode reference.
2740Sstevel@tonic-gate 	 */
2750Sstevel@tonic-gate 	if (found == TRUE) {
2760Sstevel@tonic-gate 		vp = RTOV4(rp);
2770Sstevel@tonic-gate 		VN_HOLD(vp);
2780Sstevel@tonic-gate 	}
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
2810Sstevel@tonic-gate 	nfs4_server_rele(sp);
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	if (found == FALSE) {
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 		CB_WARN("cb_getattr: bad fhandle\n");
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BADHANDLE;
2880Sstevel@tonic-gate 		return;
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	/*
2920Sstevel@tonic-gate 	 * Figure out which attributes the server wants.  We only
2930Sstevel@tonic-gate 	 * offer FATTR4_CHANGE & FATTR4_SIZE; ignore the rest.
2940Sstevel@tonic-gate 	 */
2950Sstevel@tonic-gate 	fdata = kmem_alloc(cb_getattr_bytes, KM_SLEEP);
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	/*
2980Sstevel@tonic-gate 	 * Don't actually need to create XDR to encode these
2990Sstevel@tonic-gate 	 * simple data structures.
3000Sstevel@tonic-gate 	 * xdrmem_create(&xdr, fdata, cb_getattr_bytes, XDR_ENCODE);
3010Sstevel@tonic-gate 	 */
3020Sstevel@tonic-gate 	fap = &resp->obj_attributes;
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	fap->attrmask = 0;
3050Sstevel@tonic-gate 	/* attrlist4_len starts at 0 and increases as attrs are processed */
3061232Srobinson 	fap->attrlist4 = (char *)fdata;
3070Sstevel@tonic-gate 	fap->attrlist4_len = 0;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	/* don't supply attrs if request was zero */
3100Sstevel@tonic-gate 	if (args->attr_request != 0) {
3110Sstevel@tonic-gate 		if (args->attr_request & FATTR4_CHANGE_MASK) {
3120Sstevel@tonic-gate 			/*
3130Sstevel@tonic-gate 			 * If the file is mmapped, then increment the change
3140Sstevel@tonic-gate 			 * attribute and return it.  This will guarantee that
3150Sstevel@tonic-gate 			 * the server will perceive that the file has changed
3160Sstevel@tonic-gate 			 * if there is any chance that the client application
3170Sstevel@tonic-gate 			 * has changed it.  Otherwise, just return the change
3180Sstevel@tonic-gate 			 * attribute as it has been updated by nfs4write_deleg.
3190Sstevel@tonic-gate 			 */
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 			mutex_enter(&rp->r_statelock);
3220Sstevel@tonic-gate 			mapcnt = rp->r_mapcnt;
3230Sstevel@tonic-gate 			rflag = rp->r_flags;
3240Sstevel@tonic-gate 			mutex_exit(&rp->r_statelock);
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 			mutex_enter(&rp->r_statev4_lock);
3270Sstevel@tonic-gate 			/*
3280Sstevel@tonic-gate 			 * If object mapped, then always return new change.
3290Sstevel@tonic-gate 			 * Otherwise, return change if object has dirty
3300Sstevel@tonic-gate 			 * pages.  If object doesn't have any dirty pages,
3310Sstevel@tonic-gate 			 * then all changes have been pushed to server, so
3320Sstevel@tonic-gate 			 * reset change to grant change.
3330Sstevel@tonic-gate 			 */
3340Sstevel@tonic-gate 			if (mapcnt)
3350Sstevel@tonic-gate 				rp->r_deleg_change++;
3360Sstevel@tonic-gate 			else if (! (rflag & R4DIRTY))
3370Sstevel@tonic-gate 				rp->r_deleg_change = rp->r_deleg_change_grant;
3380Sstevel@tonic-gate 			change = rp->r_deleg_change;
3390Sstevel@tonic-gate 			mutex_exit(&rp->r_statev4_lock);
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 			/*
3420Sstevel@tonic-gate 			 * Use inline XDR code directly, we know that we
3430Sstevel@tonic-gate 			 * going to a memory buffer and it has enough
3440Sstevel@tonic-gate 			 * space so it cannot fail.
3450Sstevel@tonic-gate 			 */
3460Sstevel@tonic-gate 			IXDR_PUT_U_HYPER(fdata, change);
3470Sstevel@tonic-gate 			fap->attrlist4_len += 2 * BYTES_PER_XDR_UNIT;
3481232Srobinson 			fap->attrmask |= FATTR4_CHANGE_MASK;
3490Sstevel@tonic-gate 		}
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 		if (args->attr_request & FATTR4_SIZE_MASK) {
3520Sstevel@tonic-gate 			/*
3530Sstevel@tonic-gate 			 * Use an atomic add of 0 to fetch a consistent view
3540Sstevel@tonic-gate 			 * of r_size; this avoids having to take rw_lock
3550Sstevel@tonic-gate 			 * which could cause a deadlock.
3560Sstevel@tonic-gate 			 */
3570Sstevel@tonic-gate 			size = atomic_add_64_nv((uint64_t *)&rp->r_size, 0);
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 			/*
3600Sstevel@tonic-gate 			 * Use inline XDR code directly, we know that we
3610Sstevel@tonic-gate 			 * going to a memory buffer and it has enough
3620Sstevel@tonic-gate 			 * space so it cannot fail.
3630Sstevel@tonic-gate 			 */
3640Sstevel@tonic-gate 			IXDR_PUT_U_HYPER(fdata, size);
3650Sstevel@tonic-gate 			fap->attrlist4_len += 2 * BYTES_PER_XDR_UNIT;
3661232Srobinson 			fap->attrmask |= FATTR4_SIZE_MASK;
3670Sstevel@tonic-gate 		}
3680Sstevel@tonic-gate 	}
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	VN_RELE(vp);
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
3730Sstevel@tonic-gate }
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate static void
cb_getattr_free(nfs_cb_resop4 * resop)3760Sstevel@tonic-gate cb_getattr_free(nfs_cb_resop4 *resop)
3770Sstevel@tonic-gate {
3780Sstevel@tonic-gate 	if (resop->nfs_cb_resop4_u.opcbgetattr.obj_attributes.attrlist4)
3790Sstevel@tonic-gate 		kmem_free(resop->nfs_cb_resop4_u.opcbgetattr.
3805151Swebaker 		    obj_attributes.attrlist4, cb_getattr_bytes);
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate static void
cb_recall(nfs_cb_argop4 * argop,nfs_cb_resop4 * resop,struct svc_req * req,struct compound_state * cs,struct nfs4_callback_globals * ncg)3840Sstevel@tonic-gate cb_recall(nfs_cb_argop4 *argop, nfs_cb_resop4 *resop, struct svc_req *req,
3850Sstevel@tonic-gate 	struct compound_state *cs, struct nfs4_callback_globals *ncg)
3860Sstevel@tonic-gate {
3870Sstevel@tonic-gate 	CB_RECALL4args * args = &argop->nfs_cb_argop4_u.opcbrecall;
3880Sstevel@tonic-gate 	CB_RECALL4res *resp = &resop->nfs_cb_resop4_u.opcbrecall;
3890Sstevel@tonic-gate 	rnode4_t *rp;
3900Sstevel@tonic-gate 	vnode_t *vp;
3910Sstevel@tonic-gate 	struct nfs4_server *sp;
3920Sstevel@tonic-gate 	bool_t found = FALSE;
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 	ncg->nfs4_callback_stats.cb_recall.value.ui64++;
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	ASSERT(req->rq_prog >= NFS4_CALLBACK);
3970Sstevel@tonic-gate 	ASSERT(req->rq_prog < NFS4_CALLBACK+nfs4_num_prognums);
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate #ifdef DEBUG
4000Sstevel@tonic-gate 	/*
4010Sstevel@tonic-gate 	 * error injection hook: set cb_recall_fail global to
4020Sstevel@tonic-gate 	 * NFS4 pcol error to be returned
4030Sstevel@tonic-gate 	 */
4040Sstevel@tonic-gate 	if (cb4_recall_fail != NFS4_OK) {
4050Sstevel@tonic-gate 		*cs->statusp = resp->status = cb4_recall_fail;
4060Sstevel@tonic-gate 		return;
4070Sstevel@tonic-gate 	}
4080Sstevel@tonic-gate #endif
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_cb_lock);
4110Sstevel@tonic-gate 	sp = ncg->nfs4prog2server[req->rq_prog - NFS4_CALLBACK];
4120Sstevel@tonic-gate 	mutex_exit(&ncg->nfs4_cb_lock);
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	if (nfs4_server_vlock(sp, 0) == FALSE) {
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 		CB_WARN("cb_recall: cannot find server\n");
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BADHANDLE;
4190Sstevel@tonic-gate 		return;
4200Sstevel@tonic-gate 	}
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	/*
4230Sstevel@tonic-gate 	 * Search the delegation list for a matching file handle
4240Sstevel@tonic-gate 	 * AND stateid; mutex on sp prevents the list from changing.
4250Sstevel@tonic-gate 	 */
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	rp = list_head(&sp->s_deleg_list);
4280Sstevel@tonic-gate 	for (; rp != NULL; rp = list_next(&sp->s_deleg_list, rp)) {
4290Sstevel@tonic-gate 		mutex_enter(&rp->r_statev4_lock);
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 		/* check both state id and file handle! */
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 		if ((bcmp(&rp->r_deleg_stateid, &args->stateid,
4340Sstevel@tonic-gate 		    sizeof (stateid4)) == 0)) {
4350Sstevel@tonic-gate 			nfs4_fhandle_t fhandle;
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 			sfh4_copyval(rp->r_fh, &fhandle);
4380Sstevel@tonic-gate 			if ((fhandle.fh_len == args->fh.nfs_fh4_len &&
4390Sstevel@tonic-gate 			    bcmp(fhandle.fh_buf, args->fh.nfs_fh4_val,
4400Sstevel@tonic-gate 			    fhandle.fh_len) == 0)) {
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 				found = TRUE;
4430Sstevel@tonic-gate 				break;
4440Sstevel@tonic-gate 			} else {
4450Sstevel@tonic-gate #ifdef	DEBUG
4460Sstevel@tonic-gate 				CB_WARN("cb_recall: stateid OK, bad fh");
4470Sstevel@tonic-gate #endif
4480Sstevel@tonic-gate 			}
4490Sstevel@tonic-gate 		}
4500Sstevel@tonic-gate #ifdef	DEBUG
4510Sstevel@tonic-gate 		if (bcmp(&args->stateid, &nfs4_deleg_any,
4520Sstevel@tonic-gate 		    sizeof (stateid4)) == 0) {
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 			found = TRUE;
4550Sstevel@tonic-gate 			break;
4560Sstevel@tonic-gate 		}
4570Sstevel@tonic-gate #endif
4580Sstevel@tonic-gate 		mutex_exit(&rp->r_statev4_lock);
4590Sstevel@tonic-gate 	}
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	/*
4620Sstevel@tonic-gate 	 * VN_HOLD the vnode before releasing s_lock to guarantee
4630Sstevel@tonic-gate 	 * we have a valid vnode reference.  The async thread will
4640Sstevel@tonic-gate 	 * release the hold when it's done.
4650Sstevel@tonic-gate 	 */
4660Sstevel@tonic-gate 	if (found == TRUE) {
4670Sstevel@tonic-gate 		mutex_exit(&rp->r_statev4_lock);
4680Sstevel@tonic-gate 		vp = RTOV4(rp);
4690Sstevel@tonic-gate 		VN_HOLD(vp);
4700Sstevel@tonic-gate 	}
4710Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
4720Sstevel@tonic-gate 	nfs4_server_rele(sp);
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	if (found == FALSE) {
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 		CB_WARN("cb_recall: bad stateid\n");
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
4790Sstevel@tonic-gate 		return;
4800Sstevel@tonic-gate 	}
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	/* Fire up a thread to do the delegreturn */
4830Sstevel@tonic-gate 	nfs4delegreturn_async(rp, NFS4_DR_RECALL|NFS4_DR_REOPEN,
4845151Swebaker 	    args->truncate);
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	*cs->statusp = resp->status = 0;
4870Sstevel@tonic-gate }
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate /* ARGSUSED */
4900Sstevel@tonic-gate static void
cb_recall_free(nfs_cb_resop4 * resop)4910Sstevel@tonic-gate cb_recall_free(nfs_cb_resop4 *resop)
4920Sstevel@tonic-gate {
4930Sstevel@tonic-gate 	/* nothing to do here, cb_recall doesn't kmem_alloc */
4940Sstevel@tonic-gate }
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate /*
4970Sstevel@tonic-gate  * This function handles the CB_NULL proc call from an NFSv4 Server.
4980Sstevel@tonic-gate  *
4990Sstevel@tonic-gate  * We take note that the server has sent a CB_NULL for later processing
5000Sstevel@tonic-gate  * in the recovery logic. It is noted so we may pause slightly after the
5010Sstevel@tonic-gate  * setclientid and before reopening files. The pause is to allow the
5020Sstevel@tonic-gate  * NFSv4 Server time to receive the CB_NULL reply and adjust any of
5030Sstevel@tonic-gate  * its internal structures such that it has the opportunity to grant
5040Sstevel@tonic-gate  * delegations to reopened files.
5050Sstevel@tonic-gate  *
5060Sstevel@tonic-gate  */
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate /* ARGSUSED */
5090Sstevel@tonic-gate static void
cb_null(CB_COMPOUND4args * args,CB_COMPOUND4res * resp,struct svc_req * req,struct nfs4_callback_globals * ncg)5100Sstevel@tonic-gate cb_null(CB_COMPOUND4args *args, CB_COMPOUND4res *resp, struct svc_req *req,
5110Sstevel@tonic-gate     struct nfs4_callback_globals *ncg)
5120Sstevel@tonic-gate {
5130Sstevel@tonic-gate 	struct nfs4_server *sp;
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	ncg->nfs4_callback_stats.cb_null.value.ui64++;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	ASSERT(req->rq_prog >= NFS4_CALLBACK);
5180Sstevel@tonic-gate 	ASSERT(req->rq_prog < NFS4_CALLBACK+nfs4_num_prognums);
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_cb_lock);
5210Sstevel@tonic-gate 	sp = ncg->nfs4prog2server[req->rq_prog - NFS4_CALLBACK];
5220Sstevel@tonic-gate 	mutex_exit(&ncg->nfs4_cb_lock);
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 	if (nfs4_server_vlock(sp, 0) != FALSE) {
5250Sstevel@tonic-gate 		sp->s_flags |= N4S_CB_PINGED;
5260Sstevel@tonic-gate 		cv_broadcast(&sp->wait_cb_null);
5270Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
5280Sstevel@tonic-gate 		nfs4_server_rele(sp);
5290Sstevel@tonic-gate 	}
5300Sstevel@tonic-gate }
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate /*
5330Sstevel@tonic-gate  * cb_illegal	args: void
5340Sstevel@tonic-gate  *		res : status (NFS4ERR_OP_CB_ILLEGAL)
5350Sstevel@tonic-gate  */
5360Sstevel@tonic-gate /* ARGSUSED */
5370Sstevel@tonic-gate static void
cb_illegal(nfs_cb_argop4 * argop,nfs_cb_resop4 * resop,struct svc_req * req,struct compound_state * cs,struct nfs4_callback_globals * ncg)5380Sstevel@tonic-gate cb_illegal(nfs_cb_argop4 *argop, nfs_cb_resop4 *resop, struct svc_req *req,
5390Sstevel@tonic-gate 	struct compound_state *cs, struct nfs4_callback_globals *ncg)
5400Sstevel@tonic-gate {
5410Sstevel@tonic-gate 	CB_ILLEGAL4res *resp = &resop->nfs_cb_resop4_u.opcbillegal;
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	ncg->nfs4_callback_stats.cb_illegal.value.ui64++;
5440Sstevel@tonic-gate 	resop->resop = OP_CB_ILLEGAL;
5450Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4ERR_OP_ILLEGAL;
5460Sstevel@tonic-gate }
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate static void
cb_compound(CB_COMPOUND4args * args,CB_COMPOUND4res * resp,struct svc_req * req,struct nfs4_callback_globals * ncg)5490Sstevel@tonic-gate cb_compound(CB_COMPOUND4args *args, CB_COMPOUND4res *resp, struct svc_req *req,
5500Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg)
5510Sstevel@tonic-gate {
5520Sstevel@tonic-gate 	uint_t i;
5530Sstevel@tonic-gate 	struct compound_state cs;
5540Sstevel@tonic-gate 	nfs_cb_argop4 *argop;
5550Sstevel@tonic-gate 	nfs_cb_resop4 *resop, *new_res;
5560Sstevel@tonic-gate 	uint_t op;
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	bzero(&cs, sizeof (cs));
5590Sstevel@tonic-gate 	cs.statusp = &resp->status;
5600Sstevel@tonic-gate 	cs.cont = TRUE;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	/*
5630Sstevel@tonic-gate 	 * Form a reply tag by copying over the reqeuest tag.
5640Sstevel@tonic-gate 	 */
5650Sstevel@tonic-gate 	resp->tag.utf8string_len = args->tag.utf8string_len;
5660Sstevel@tonic-gate 	resp->tag.utf8string_val = kmem_alloc(resp->tag.utf8string_len,
5675151Swebaker 	    KM_SLEEP);
5680Sstevel@tonic-gate 	bcopy(args->tag.utf8string_val, resp->tag.utf8string_val,
5695151Swebaker 	    args->tag.utf8string_len);
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	/*
5720Sstevel@tonic-gate 	 * XXX for now, minorversion should be zero
5730Sstevel@tonic-gate 	 */
5740Sstevel@tonic-gate 	if (args->minorversion != CB4_MINORVERSION) {
5750Sstevel@tonic-gate 		resp->array_len = 0;
5760Sstevel@tonic-gate 		resp->array = NULL;
5770Sstevel@tonic-gate 		resp->status = NFS4ERR_MINOR_VERS_MISMATCH;
5780Sstevel@tonic-gate 		return;
5790Sstevel@tonic-gate 	}
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate #ifdef DEBUG
5820Sstevel@tonic-gate 	/*
5830Sstevel@tonic-gate 	 * Verify callback_ident.  It doesn't really matter if it's wrong
5840Sstevel@tonic-gate 	 * because we don't really use callback_ident -- we use prog number
5850Sstevel@tonic-gate 	 * of the RPC request instead.  In this case, just print a DEBUG
5860Sstevel@tonic-gate 	 * console message to reveal brokenness of cbclient (at bkoff/cthon).
5870Sstevel@tonic-gate 	 */
5880Sstevel@tonic-gate 	if (args->callback_ident != req->rq_prog)
5890Sstevel@tonic-gate 		zcmn_err(getzoneid(), CE_WARN,
5900Sstevel@tonic-gate 		    "cb_compound: cb_client using wrong "
5910Sstevel@tonic-gate 		    "callback_ident(%d), should be %d",
5920Sstevel@tonic-gate 		    args->callback_ident, req->rq_prog);
5930Sstevel@tonic-gate #endif
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	resp->array_len = args->array_len;
5960Sstevel@tonic-gate 	resp->array = kmem_zalloc(args->array_len * sizeof (nfs_cb_resop4),
5975151Swebaker 	    KM_SLEEP);
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 	for (i = 0; i < args->array_len && cs.cont; i++) {
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 		argop = &args->array[i];
6020Sstevel@tonic-gate 		resop = &resp->array[i];
6030Sstevel@tonic-gate 		resop->resop = argop->argop;
6040Sstevel@tonic-gate 		op = (uint_t)resop->resop;
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 		switch (op) {
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 		case OP_CB_GETATTR:
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 			cb_getattr(argop, resop, req, &cs, ncg);
6110Sstevel@tonic-gate 			break;
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 		case OP_CB_RECALL:
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 			cb_recall(argop, resop, req, &cs, ncg);
6160Sstevel@tonic-gate 			break;
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 		case OP_CB_ILLEGAL:
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 			/* fall through */
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 		default:
6230Sstevel@tonic-gate 			/*
6240Sstevel@tonic-gate 			 * Handle OP_CB_ILLEGAL and any undefined opcode.
6250Sstevel@tonic-gate 			 * Currently, the XDR code will return BADXDR
6260Sstevel@tonic-gate 			 * if cb op doesn't decode to legal value, so
6270Sstevel@tonic-gate 			 * it really only handles OP_CB_ILLEGAL.
6280Sstevel@tonic-gate 			 */
6290Sstevel@tonic-gate 			op = OP_CB_ILLEGAL;
6300Sstevel@tonic-gate 			cb_illegal(argop, resop, req, &cs, ncg);
6310Sstevel@tonic-gate 		}
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 		if (*cs.statusp != NFS4_OK)
6340Sstevel@tonic-gate 			cs.cont = FALSE;
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 		/*
6370Sstevel@tonic-gate 		 * If not at last op, and if we are to stop, then
6380Sstevel@tonic-gate 		 * compact the results array.
6390Sstevel@tonic-gate 		 */
6400Sstevel@tonic-gate 		if ((i + 1) < args->array_len && !cs.cont) {
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 			new_res = kmem_alloc(
6435151Swebaker 			    (i+1) * sizeof (nfs_cb_resop4), KM_SLEEP);
6440Sstevel@tonic-gate 			bcopy(resp->array,
6455151Swebaker 			    new_res, (i+1) * sizeof (nfs_cb_resop4));
6460Sstevel@tonic-gate 			kmem_free(resp->array,
6475151Swebaker 			    args->array_len * sizeof (nfs_cb_resop4));
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 			resp->array_len =  i + 1;
6500Sstevel@tonic-gate 			resp->array = new_res;
6510Sstevel@tonic-gate 		}
6520Sstevel@tonic-gate 	}
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate }
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate static void
cb_compound_free(CB_COMPOUND4res * resp)6570Sstevel@tonic-gate cb_compound_free(CB_COMPOUND4res *resp)
6580Sstevel@tonic-gate {
6590Sstevel@tonic-gate 	uint_t i, op;
6600Sstevel@tonic-gate 	nfs_cb_resop4 *resop;
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	if (resp->tag.utf8string_val) {
6630Sstevel@tonic-gate 		UTF8STRING_FREE(resp->tag)
6640Sstevel@tonic-gate 	}
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	for (i = 0; i < resp->array_len; i++) {
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 		resop = &resp->array[i];
6690Sstevel@tonic-gate 		op = (uint_t)resop->resop;
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 		switch (op) {
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 		case OP_CB_GETATTR:
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 			cb_getattr_free(resop);
6760Sstevel@tonic-gate 			break;
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 		case OP_CB_RECALL:
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 			cb_recall_free(resop);
6810Sstevel@tonic-gate 			break;
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 		default:
6840Sstevel@tonic-gate 			break;
6850Sstevel@tonic-gate 		}
6860Sstevel@tonic-gate 	}
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 	if (resp->array != NULL) {
6890Sstevel@tonic-gate 		kmem_free(resp->array,
6905151Swebaker 		    resp->array_len * sizeof (nfs_cb_resop4));
6910Sstevel@tonic-gate 	}
6920Sstevel@tonic-gate }
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate static void
cb_dispatch(struct svc_req * req,SVCXPRT * xprt)6950Sstevel@tonic-gate cb_dispatch(struct svc_req *req, SVCXPRT *xprt)
6960Sstevel@tonic-gate {
6970Sstevel@tonic-gate 	CB_COMPOUND4args args;
6980Sstevel@tonic-gate 	CB_COMPOUND4res res;
6990Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	bool_t (*xdr_args)(), (*xdr_res)();
7020Sstevel@tonic-gate 	void (*proc)(CB_COMPOUND4args *, CB_COMPOUND4res *, struct svc_req *,
7030Sstevel@tonic-gate 	    struct nfs4_callback_globals *);
7040Sstevel@tonic-gate 	void (*freeproc)(CB_COMPOUND4res *);
7050Sstevel@tonic-gate 
706766Scarlsonj 	ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone());
7070Sstevel@tonic-gate 	ASSERT(ncg != NULL);
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	ncg->nfs4_callback_stats.cb_dispatch.value.ui64++;
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	switch (req->rq_proc) {
7120Sstevel@tonic-gate 	case CB_NULL:
7130Sstevel@tonic-gate 		xdr_args = xdr_void;
7140Sstevel@tonic-gate 		xdr_res = xdr_void;
7150Sstevel@tonic-gate 		proc = cb_null;
7160Sstevel@tonic-gate 		freeproc = NULL;
7170Sstevel@tonic-gate 		break;
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 	case CB_COMPOUND:
7201232Srobinson 		xdr_args = xdr_CB_COMPOUND4args_clnt;
7210Sstevel@tonic-gate 		xdr_res = xdr_CB_COMPOUND4res;
7220Sstevel@tonic-gate 		proc = cb_compound;
7230Sstevel@tonic-gate 		freeproc = cb_compound_free;
7240Sstevel@tonic-gate 		break;
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	default:
7270Sstevel@tonic-gate 		CB_WARN("cb_dispatch: no proc\n");
7280Sstevel@tonic-gate 		svcerr_noproc(xprt);
7290Sstevel@tonic-gate 		return;
7300Sstevel@tonic-gate 	}
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	args.tag.utf8string_val = NULL;
7330Sstevel@tonic-gate 	args.array = NULL;
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate 	if (!SVC_GETARGS(xprt, xdr_args, (caddr_t)&args)) {
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 		CB_WARN("cb_dispatch: cannot getargs\n");
7380Sstevel@tonic-gate 		svcerr_decode(xprt);
7390Sstevel@tonic-gate 		return;
7400Sstevel@tonic-gate 	}
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 	(*proc)(&args, &res, req, ncg);
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	if (svc_sendreply(xprt, xdr_res, (caddr_t)&res) == FALSE) {
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 		CB_WARN("cb_dispatch: bad sendreply\n");
7476204Srmesta 		svcerr_systemerr(xprt);
7480Sstevel@tonic-gate 	}
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 	if (freeproc)
7510Sstevel@tonic-gate 		(*freeproc)(&res);
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	if (!SVC_FREEARGS(xprt, xdr_args, (caddr_t)&args)) {
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 		CB_WARN("cb_dispatch: bad freeargs\n");
7560Sstevel@tonic-gate 	}
7570Sstevel@tonic-gate }
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate static rpcprog_t
nfs4_getnextprogram(struct nfs4_callback_globals * ncg)7600Sstevel@tonic-gate nfs4_getnextprogram(struct nfs4_callback_globals *ncg)
7610Sstevel@tonic-gate {
7620Sstevel@tonic-gate 	int i, j;
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 	j = ncg->nfs4_program_hint;
7650Sstevel@tonic-gate 	for (i = 0; i < nfs4_num_prognums; i++, j++) {
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 		if (j >= nfs4_num_prognums)
7680Sstevel@tonic-gate 			j = 0;
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 		if (ncg->nfs4prog2server[j] == NULL) {
7710Sstevel@tonic-gate 			ncg->nfs4_program_hint = j+1;
7720Sstevel@tonic-gate 			return (j+NFS4_CALLBACK);
7730Sstevel@tonic-gate 		}
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	return (0);
7770Sstevel@tonic-gate }
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate void
nfs4callback_destroy(nfs4_server_t * np)7800Sstevel@tonic-gate nfs4callback_destroy(nfs4_server_t *np)
7810Sstevel@tonic-gate {
7820Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
7830Sstevel@tonic-gate 	int i;
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 	if (np->s_program == 0)
7860Sstevel@tonic-gate 		return;
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 	ncg = np->zone_globals;
7890Sstevel@tonic-gate 	i = np->s_program - NFS4_CALLBACK;
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_cb_lock);
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	ASSERT(ncg->nfs4prog2server[i] == np);
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	ncg->nfs4prog2server[i] = NULL;
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate 	if (i < ncg->nfs4_program_hint)
7980Sstevel@tonic-gate 		ncg->nfs4_program_hint = i;
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	mutex_exit(&ncg->nfs4_cb_lock);
8010Sstevel@tonic-gate }
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate /*
8040Sstevel@tonic-gate  * nfs4_setport - This function saves a netid and univeral address for
8050Sstevel@tonic-gate  * the callback program.  These values will be used during setclientid.
8060Sstevel@tonic-gate  */
8070Sstevel@tonic-gate static void
nfs4_setport(char * netid,char * uaddr,char * protofmly,char * proto,struct nfs4_callback_globals * ncg)8080Sstevel@tonic-gate nfs4_setport(char *netid, char *uaddr, char *protofmly, char *proto,
8090Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg)
8100Sstevel@tonic-gate {
8110Sstevel@tonic-gate 	struct nfs4_cb_port *p;
8120Sstevel@tonic-gate 	bool_t found = FALSE;
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ncg->nfs4_cb_lock));
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 	p = list_head(&ncg->nfs4_cb_ports);
8170Sstevel@tonic-gate 	for (; p != NULL; p = list_next(&ncg->nfs4_cb_ports, p)) {
8180Sstevel@tonic-gate 		if (strcmp(p->netid, netid) == 0) {
8190Sstevel@tonic-gate 			found = TRUE;
8200Sstevel@tonic-gate 			break;
8210Sstevel@tonic-gate 		}
8220Sstevel@tonic-gate 	}
8230Sstevel@tonic-gate 	if (found == TRUE)
8240Sstevel@tonic-gate 		(void) strcpy(p->uaddr, uaddr);
8250Sstevel@tonic-gate 	else {
8260Sstevel@tonic-gate 		p = kmem_alloc(sizeof (*p), KM_SLEEP);
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 		(void) strcpy(p->uaddr, uaddr);
8290Sstevel@tonic-gate 		(void) strcpy(p->netid, netid);
8300Sstevel@tonic-gate 		(void) strcpy(p->protofmly, protofmly);
8310Sstevel@tonic-gate 		(void) strcpy(p->proto, proto);
8320Sstevel@tonic-gate 		list_insert_head(&ncg->nfs4_cb_ports, p);
8330Sstevel@tonic-gate 	}
8340Sstevel@tonic-gate }
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate /*
8370Sstevel@tonic-gate  * nfs4_cb_args - This function is used to construct the callback
8380Sstevel@tonic-gate  * portion of the arguments needed for setclientid.
8390Sstevel@tonic-gate  */
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate void
nfs4_cb_args(nfs4_server_t * np,struct knetconfig * knc,SETCLIENTID4args * args)8420Sstevel@tonic-gate nfs4_cb_args(nfs4_server_t *np, struct knetconfig *knc, SETCLIENTID4args *args)
8430Sstevel@tonic-gate {
8440Sstevel@tonic-gate 	struct nfs4_cb_port *p;
8450Sstevel@tonic-gate 	bool_t found = FALSE;
8460Sstevel@tonic-gate 	rpcprog_t pgm;
8470Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg = np->zone_globals;
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	/*
8500Sstevel@tonic-gate 	 * This server structure may already have a program number
8510Sstevel@tonic-gate 	 * assigned to it.  This happens when the client has to
8520Sstevel@tonic-gate 	 * re-issue SETCLIENTID.  Just re-use the information.
8530Sstevel@tonic-gate 	 */
8540Sstevel@tonic-gate 	if (np->s_program >= NFS4_CALLBACK &&
8550Sstevel@tonic-gate 	    np->s_program < NFS4_CALLBACK + nfs4_num_prognums)
8560Sstevel@tonic-gate 		nfs4callback_destroy(np);
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_cb_lock);
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 	p = list_head(&ncg->nfs4_cb_ports);
8610Sstevel@tonic-gate 	for (; p != NULL; p = list_next(&ncg->nfs4_cb_ports, p)) {
8620Sstevel@tonic-gate 		if (strcmp(p->protofmly, knc->knc_protofmly) == 0 &&
8630Sstevel@tonic-gate 		    strcmp(p->proto, knc->knc_proto) == 0) {
8640Sstevel@tonic-gate 			found = TRUE;
8650Sstevel@tonic-gate 			break;
8660Sstevel@tonic-gate 		}
8670Sstevel@tonic-gate 	}
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	if (found == FALSE) {
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_callback_debug,
8725151Swebaker 		    (CE_WARN, "nfs4_cb_args: could not find netid for %s/%s\n",
8735151Swebaker 		    knc->knc_protofmly, knc->knc_proto));
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 		args->callback.cb_program = 0;
8760Sstevel@tonic-gate 		args->callback.cb_location.r_netid = NULL;
8770Sstevel@tonic-gate 		args->callback.cb_location.r_addr = NULL;
8780Sstevel@tonic-gate 		args->callback_ident = 0;
8790Sstevel@tonic-gate 		mutex_exit(&ncg->nfs4_cb_lock);
8800Sstevel@tonic-gate 		return;
8810Sstevel@tonic-gate 	}
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	if ((pgm = nfs4_getnextprogram(ncg)) == 0) {
8840Sstevel@tonic-gate 		CB_WARN("nfs4_cb_args: out of program numbers\n");
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 		args->callback.cb_program = 0;
8870Sstevel@tonic-gate 		args->callback.cb_location.r_netid = NULL;
8880Sstevel@tonic-gate 		args->callback.cb_location.r_addr = NULL;
8890Sstevel@tonic-gate 		args->callback_ident = 0;
8900Sstevel@tonic-gate 		mutex_exit(&ncg->nfs4_cb_lock);
8910Sstevel@tonic-gate 		return;
8920Sstevel@tonic-gate 	}
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	ncg->nfs4prog2server[pgm-NFS4_CALLBACK] = np;
8950Sstevel@tonic-gate 	args->callback.cb_program = pgm;
8960Sstevel@tonic-gate 	args->callback.cb_location.r_netid = p->netid;
8970Sstevel@tonic-gate 	args->callback.cb_location.r_addr = p->uaddr;
8980Sstevel@tonic-gate 	args->callback_ident = pgm;
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 	np->s_program = pgm;
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	mutex_exit(&ncg->nfs4_cb_lock);
9030Sstevel@tonic-gate }
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate static int
nfs4_dquery(struct nfs4_svc_args * arg,model_t model)9060Sstevel@tonic-gate nfs4_dquery(struct nfs4_svc_args *arg, model_t model)
9070Sstevel@tonic-gate {
9080Sstevel@tonic-gate 	file_t *fp;
9090Sstevel@tonic-gate 	vnode_t *vp;
9100Sstevel@tonic-gate 	rnode4_t *rp;
9110Sstevel@tonic-gate 	int error;
9120Sstevel@tonic-gate 	STRUCT_HANDLE(nfs4_svc_args, uap);
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 	STRUCT_SET_HANDLE(uap, model, arg);
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	if ((fp = getf(STRUCT_FGET(uap, fd))) == NULL)
9170Sstevel@tonic-gate 		return (EBADF);
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	vp = fp->f_vnode;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	if (vp == NULL || vp->v_type != VREG ||
9220Sstevel@tonic-gate 	    !vn_matchops(vp, nfs4_vnodeops)) {
9230Sstevel@tonic-gate 		releasef(STRUCT_FGET(uap, fd));
9240Sstevel@tonic-gate 		return (EBADF);
9250Sstevel@tonic-gate 	}
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 	rp = VTOR4(vp);
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	/*
9300Sstevel@tonic-gate 	 * I can't convince myself that we need locking here.  The
9310Sstevel@tonic-gate 	 * rnode cannot disappear and the value returned is instantly
9320Sstevel@tonic-gate 	 * stale anway, so why bother?
9330Sstevel@tonic-gate 	 */
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	error = suword32(STRUCT_FGETP(uap, netid), rp->r_deleg_type);
9360Sstevel@tonic-gate 	releasef(STRUCT_FGET(uap, fd));
9370Sstevel@tonic-gate 	return (error);
9380Sstevel@tonic-gate }
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate /*
9420Sstevel@tonic-gate  * NFS4 client system call.  This service does the
9430Sstevel@tonic-gate  * necessary initialization for the callback program.
9440Sstevel@tonic-gate  * This is fashioned after the server side interaction
9450Sstevel@tonic-gate  * between nfsd and the kernel.  On the client, the
9460Sstevel@tonic-gate  * mount command forks and the child process does the
9470Sstevel@tonic-gate  * necessary interaction with the kernel.
9480Sstevel@tonic-gate  *
9490Sstevel@tonic-gate  * uap->fd is the fd of an open transport provider
9500Sstevel@tonic-gate  */
9510Sstevel@tonic-gate int
nfs4_svc(struct nfs4_svc_args * arg,model_t model)9520Sstevel@tonic-gate nfs4_svc(struct nfs4_svc_args *arg, model_t model)
9530Sstevel@tonic-gate {
9540Sstevel@tonic-gate 	file_t *fp;
9550Sstevel@tonic-gate 	int error;
9560Sstevel@tonic-gate 	int readsize;
9570Sstevel@tonic-gate 	char buf[KNC_STRSIZE], uaddr[KNC_STRSIZE];
9580Sstevel@tonic-gate 	char protofmly[KNC_STRSIZE], proto[KNC_STRSIZE];
9590Sstevel@tonic-gate 	size_t len;
9600Sstevel@tonic-gate 	STRUCT_HANDLE(nfs4_svc_args, uap);
9610Sstevel@tonic-gate 	struct netbuf addrmask;
9620Sstevel@tonic-gate 	int cmd;
9630Sstevel@tonic-gate 	SVCMASTERXPRT *cb_xprt;
9640Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate #ifdef lint
9670Sstevel@tonic-gate 	model = model;		/* STRUCT macros don't always refer to it */
9680Sstevel@tonic-gate #endif
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	STRUCT_SET_HANDLE(uap, model, arg);
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 	if (STRUCT_FGET(uap, cmd) == NFS4_DQUERY)
9730Sstevel@tonic-gate 		return (nfs4_dquery(arg, model));
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 	if (secpolicy_nfs(CRED()) != 0)
9760Sstevel@tonic-gate 		return (EPERM);
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	if ((fp = getf(STRUCT_FGET(uap, fd))) == NULL)
9790Sstevel@tonic-gate 		return (EBADF);
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	/*
9820Sstevel@tonic-gate 	 * Set read buffer size to rsize
9830Sstevel@tonic-gate 	 * and add room for RPC headers.
9840Sstevel@tonic-gate 	 */
9850Sstevel@tonic-gate 	readsize = nfs3tsize() + (RPC_MAXDATASIZE - NFS_MAXDATA);
9860Sstevel@tonic-gate 	if (readsize < RPC_MAXDATASIZE)
9870Sstevel@tonic-gate 		readsize = RPC_MAXDATASIZE;
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 	error = copyinstr((const char *)STRUCT_FGETP(uap, netid), buf,
9900Sstevel@tonic-gate 	    KNC_STRSIZE, &len);
9910Sstevel@tonic-gate 	if (error) {
9920Sstevel@tonic-gate 		releasef(STRUCT_FGET(uap, fd));
9930Sstevel@tonic-gate 		return (error);
9940Sstevel@tonic-gate 	}
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	cmd = STRUCT_FGET(uap, cmd);
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 	if (cmd & NFS4_KRPC_START) {
9990Sstevel@tonic-gate 		addrmask.len = STRUCT_FGET(uap, addrmask.len);
10000Sstevel@tonic-gate 		addrmask.maxlen = STRUCT_FGET(uap, addrmask.maxlen);
10010Sstevel@tonic-gate 		addrmask.buf = kmem_alloc(addrmask.maxlen, KM_SLEEP);
10020Sstevel@tonic-gate 		error = copyin(STRUCT_FGETP(uap, addrmask.buf), addrmask.buf,
10030Sstevel@tonic-gate 		    addrmask.len);
10040Sstevel@tonic-gate 		if (error) {
10050Sstevel@tonic-gate 			releasef(STRUCT_FGET(uap, fd));
10060Sstevel@tonic-gate 			kmem_free(addrmask.buf, addrmask.maxlen);
10070Sstevel@tonic-gate 			return (error);
10080Sstevel@tonic-gate 		}
10090Sstevel@tonic-gate 	}
10100Sstevel@tonic-gate 	else
10110Sstevel@tonic-gate 		addrmask.buf = NULL;
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate 	error = copyinstr((const char *)STRUCT_FGETP(uap, addr), uaddr,
10140Sstevel@tonic-gate 	    sizeof (uaddr), &len);
10150Sstevel@tonic-gate 	if (error) {
10160Sstevel@tonic-gate 		releasef(STRUCT_FGET(uap, fd));
10170Sstevel@tonic-gate 		if (addrmask.buf)
10180Sstevel@tonic-gate 			kmem_free(addrmask.buf, addrmask.maxlen);
10190Sstevel@tonic-gate 		return (error);
10200Sstevel@tonic-gate 	}
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	error = copyinstr((const char *)STRUCT_FGETP(uap, protofmly), protofmly,
10230Sstevel@tonic-gate 	    sizeof (protofmly), &len);
10240Sstevel@tonic-gate 	if (error) {
10250Sstevel@tonic-gate 		releasef(STRUCT_FGET(uap, fd));
10260Sstevel@tonic-gate 		if (addrmask.buf)
10270Sstevel@tonic-gate 			kmem_free(addrmask.buf, addrmask.maxlen);
10280Sstevel@tonic-gate 		return (error);
10290Sstevel@tonic-gate 	}
10300Sstevel@tonic-gate 
10310Sstevel@tonic-gate 	error = copyinstr((const char *)STRUCT_FGETP(uap, proto), proto,
10320Sstevel@tonic-gate 	    sizeof (proto), &len);
10330Sstevel@tonic-gate 	if (error) {
10340Sstevel@tonic-gate 		releasef(STRUCT_FGET(uap, fd));
10350Sstevel@tonic-gate 		if (addrmask.buf)
10360Sstevel@tonic-gate 			kmem_free(addrmask.buf, addrmask.maxlen);
10370Sstevel@tonic-gate 		return (error);
10380Sstevel@tonic-gate 	}
10390Sstevel@tonic-gate 
1040766Scarlsonj 	ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone());
10410Sstevel@tonic-gate 	ASSERT(ncg != NULL);
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_cb_lock);
10440Sstevel@tonic-gate 	if (cmd & NFS4_SETPORT)
10450Sstevel@tonic-gate 		nfs4_setport(buf, uaddr, protofmly, proto, ncg);
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 	if (cmd & NFS4_KRPC_START) {
10480Sstevel@tonic-gate 		error = svc_tli_kcreate(fp, readsize, buf, &addrmask, &cb_xprt,
10490Sstevel@tonic-gate 		    &nfs4_cb_sct, NULL, NFS_CB_SVCPOOL_ID, FALSE);
10500Sstevel@tonic-gate 		if (error) {
10510Sstevel@tonic-gate 			CB_WARN1("nfs4_svc: svc_tli_kcreate failed %d\n",
10525151Swebaker 			    error);
10530Sstevel@tonic-gate 			kmem_free(addrmask.buf, addrmask.maxlen);
10540Sstevel@tonic-gate 		}
10550Sstevel@tonic-gate 	}
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 	mutex_exit(&ncg->nfs4_cb_lock);
10580Sstevel@tonic-gate 	releasef(STRUCT_FGET(uap, fd));
10590Sstevel@tonic-gate 	return (error);
10600Sstevel@tonic-gate }
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate struct nfs4_callback_globals *
nfs4_get_callback_globals(void)10630Sstevel@tonic-gate nfs4_get_callback_globals(void)
10640Sstevel@tonic-gate {
1065766Scarlsonj 	return (zone_getspecific(nfs4_callback_zone_key, nfs_zone()));
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate static void *
nfs4_callback_init_zone(zoneid_t zoneid)10690Sstevel@tonic-gate nfs4_callback_init_zone(zoneid_t zoneid)
10700Sstevel@tonic-gate {
10710Sstevel@tonic-gate 	kstat_t *nfs4_callback_kstat;
10720Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 	ncg = kmem_zalloc(sizeof (*ncg), KM_SLEEP);
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 	ncg->nfs4prog2server = kmem_zalloc(nfs4_num_prognums *
10775151Swebaker 	    sizeof (struct nfs4_server *), KM_SLEEP);
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	/* initialize the dlist */
10800Sstevel@tonic-gate 	mutex_init(&ncg->nfs4_dlist_lock, NULL, MUTEX_DEFAULT, NULL);
10810Sstevel@tonic-gate 	list_create(&ncg->nfs4_dlist, sizeof (struct nfs4_dnode),
10820Sstevel@tonic-gate 	    offsetof(struct nfs4_dnode, linkage));
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate 	/* initialize cb_port list */
10850Sstevel@tonic-gate 	mutex_init(&ncg->nfs4_cb_lock, NULL, MUTEX_DEFAULT, NULL);
10860Sstevel@tonic-gate 	list_create(&ncg->nfs4_cb_ports, sizeof (struct nfs4_cb_port),
10870Sstevel@tonic-gate 	    offsetof(struct nfs4_cb_port, linkage));
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate 	/* get our own copy of the kstats */
10900Sstevel@tonic-gate 	bcopy(&nfs4_callback_stats_tmpl, &ncg->nfs4_callback_stats,
10910Sstevel@tonic-gate 	    sizeof (nfs4_callback_stats_tmpl));
10920Sstevel@tonic-gate 	/* register "nfs:0:nfs4_callback_stats" for this zone */
10930Sstevel@tonic-gate 	if ((nfs4_callback_kstat =
10945151Swebaker 	    kstat_create_zone("nfs", 0, "nfs4_callback_stats", "misc",
10955151Swebaker 	    KSTAT_TYPE_NAMED,
10965151Swebaker 	    sizeof (ncg->nfs4_callback_stats) / sizeof (kstat_named_t),
10975151Swebaker 	    KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE,
10985151Swebaker 	    zoneid)) != NULL) {
10990Sstevel@tonic-gate 		nfs4_callback_kstat->ks_data = &ncg->nfs4_callback_stats;
11000Sstevel@tonic-gate 		kstat_install(nfs4_callback_kstat);
11010Sstevel@tonic-gate 	}
11020Sstevel@tonic-gate 	return (ncg);
11030Sstevel@tonic-gate }
11040Sstevel@tonic-gate 
11050Sstevel@tonic-gate static void
nfs4_discard_delegations(struct nfs4_callback_globals * ncg)11060Sstevel@tonic-gate nfs4_discard_delegations(struct nfs4_callback_globals *ncg)
11070Sstevel@tonic-gate {
11080Sstevel@tonic-gate 	nfs4_server_t *sp;
11090Sstevel@tonic-gate 	int i, num_removed;
11100Sstevel@tonic-gate 
11110Sstevel@tonic-gate 	/*
11120Sstevel@tonic-gate 	 * It's OK here to just run through the registered "programs", as
11130Sstevel@tonic-gate 	 * servers without programs won't have any delegations to handle.
11140Sstevel@tonic-gate 	 */
11150Sstevel@tonic-gate 	for (i = 0; i < nfs4_num_prognums; i++) {
11160Sstevel@tonic-gate 		rnode4_t *rp;
11170Sstevel@tonic-gate 
11180Sstevel@tonic-gate 		mutex_enter(&ncg->nfs4_cb_lock);
11190Sstevel@tonic-gate 		sp = ncg->nfs4prog2server[i];
11200Sstevel@tonic-gate 		mutex_exit(&ncg->nfs4_cb_lock);
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 		if (nfs4_server_vlock(sp, 1) == FALSE)
11230Sstevel@tonic-gate 			continue;
11240Sstevel@tonic-gate 		num_removed = 0;
11250Sstevel@tonic-gate 		while ((rp = list_head(&sp->s_deleg_list)) != NULL) {
11260Sstevel@tonic-gate 			mutex_enter(&rp->r_statev4_lock);
11270Sstevel@tonic-gate 			if (rp->r_deleg_type == OPEN_DELEGATE_NONE) {
11280Sstevel@tonic-gate 				/*
11290Sstevel@tonic-gate 				 * We need to take matters into our own hands,
11300Sstevel@tonic-gate 				 * as nfs4delegreturn_cleanup_impl() won't
11310Sstevel@tonic-gate 				 * remove this from the list.
11320Sstevel@tonic-gate 				 */
11330Sstevel@tonic-gate 				list_remove(&sp->s_deleg_list, rp);
11340Sstevel@tonic-gate 				mutex_exit(&rp->r_statev4_lock);
11350Sstevel@tonic-gate 				nfs4_dec_state_ref_count_nolock(sp,
11360Sstevel@tonic-gate 				    VTOMI4(RTOV4(rp)));
11370Sstevel@tonic-gate 				num_removed++;
11380Sstevel@tonic-gate 				continue;
11390Sstevel@tonic-gate 			}
11400Sstevel@tonic-gate 			mutex_exit(&rp->r_statev4_lock);
11410Sstevel@tonic-gate 			VN_HOLD(RTOV4(rp));
11420Sstevel@tonic-gate 			mutex_exit(&sp->s_lock);
11430Sstevel@tonic-gate 			/*
11440Sstevel@tonic-gate 			 * The following will remove the node from the list.
11450Sstevel@tonic-gate 			 */
11460Sstevel@tonic-gate 			nfs4delegreturn_cleanup_impl(rp, sp, ncg);
11470Sstevel@tonic-gate 			VN_RELE(RTOV4(rp));
11480Sstevel@tonic-gate 			mutex_enter(&sp->s_lock);
11490Sstevel@tonic-gate 		}
11500Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
11510Sstevel@tonic-gate 		/* each removed list node reles a reference */
11520Sstevel@tonic-gate 		while (num_removed-- > 0)
11530Sstevel@tonic-gate 			nfs4_server_rele(sp);
11540Sstevel@tonic-gate 		/* remove our reference for nfs4_server_vlock */
11550Sstevel@tonic-gate 		nfs4_server_rele(sp);
11560Sstevel@tonic-gate 	}
11570Sstevel@tonic-gate }
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate /* ARGSUSED */
11600Sstevel@tonic-gate static void
nfs4_callback_shutdown_zone(zoneid_t zoneid,void * data)11610Sstevel@tonic-gate nfs4_callback_shutdown_zone(zoneid_t zoneid, void *data)
11620Sstevel@tonic-gate {
11630Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg = data;
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 	/*
11660Sstevel@tonic-gate 	 * Clean pending delegation return list.
11670Sstevel@tonic-gate 	 */
11680Sstevel@tonic-gate 	nfs4_dlistclean_impl(ncg, NFS4_DR_DISCARD);
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 	/*
11710Sstevel@tonic-gate 	 * Discard all delegations.
11720Sstevel@tonic-gate 	 */
11730Sstevel@tonic-gate 	nfs4_discard_delegations(ncg);
11740Sstevel@tonic-gate }
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate static void
nfs4_callback_fini_zone(zoneid_t zoneid,void * data)11770Sstevel@tonic-gate nfs4_callback_fini_zone(zoneid_t zoneid, void *data)
11780Sstevel@tonic-gate {
11790Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg = data;
11800Sstevel@tonic-gate 	struct nfs4_cb_port *p;
11810Sstevel@tonic-gate 	nfs4_server_t *sp, *next;
11820Sstevel@tonic-gate 	nfs4_server_t freelist;
11830Sstevel@tonic-gate 	int i;
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate 	kstat_delete_byname_zone("nfs", 0, "nfs4_callback_stats", zoneid);
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 	/*
11880Sstevel@tonic-gate 	 * Discard all delegations that may have crept in since we did the
11890Sstevel@tonic-gate 	 * _shutdown.
11900Sstevel@tonic-gate 	 */
11910Sstevel@tonic-gate 	nfs4_discard_delegations(ncg);
11920Sstevel@tonic-gate 	/*
11930Sstevel@tonic-gate 	 * We're completely done with this zone and all associated
11940Sstevel@tonic-gate 	 * nfs4_server_t's.  Any remaining nfs4_server_ts should only have one
11950Sstevel@tonic-gate 	 * more reference outstanding -- the reference we didn't release in
11960Sstevel@tonic-gate 	 * nfs4_renew_lease_thread().
11970Sstevel@tonic-gate 	 *
11980Sstevel@tonic-gate 	 * Here we need to run through the global nfs4_server_lst as we need to
11990Sstevel@tonic-gate 	 * deal with nfs4_server_ts without programs, as they also have threads
12000Sstevel@tonic-gate 	 * created for them, and so have outstanding references that we need to
12010Sstevel@tonic-gate 	 * release.
12020Sstevel@tonic-gate 	 */
12030Sstevel@tonic-gate 	freelist.forw = &freelist;
12040Sstevel@tonic-gate 	freelist.back = &freelist;
12050Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
12060Sstevel@tonic-gate 	sp = nfs4_server_lst.forw;
12070Sstevel@tonic-gate 	while (sp != &nfs4_server_lst) {
12080Sstevel@tonic-gate 		next = sp->forw;
12090Sstevel@tonic-gate 		if (sp->zoneid == zoneid) {
12100Sstevel@tonic-gate 			remque(sp);
12110Sstevel@tonic-gate 			insque(sp, &freelist);
12120Sstevel@tonic-gate 		}
12130Sstevel@tonic-gate 		sp = next;
12140Sstevel@tonic-gate 	}
12150Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 	sp = freelist.forw;
12180Sstevel@tonic-gate 	while (sp != &freelist) {
12190Sstevel@tonic-gate 		next = sp->forw;
12200Sstevel@tonic-gate 		nfs4_server_rele(sp);	/* free the list's reference */
12210Sstevel@tonic-gate 		sp = next;
12220Sstevel@tonic-gate 	}
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate #ifdef DEBUG
12250Sstevel@tonic-gate 	for (i = 0; i < nfs4_num_prognums; i++) {
12260Sstevel@tonic-gate 		ASSERT(ncg->nfs4prog2server[i] == NULL);
12270Sstevel@tonic-gate 	}
12280Sstevel@tonic-gate #endif
12290Sstevel@tonic-gate 	kmem_free(ncg->nfs4prog2server, nfs4_num_prognums *
12300Sstevel@tonic-gate 	    sizeof (struct nfs4_server *));
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_cb_lock);
12330Sstevel@tonic-gate 	while ((p = list_head(&ncg->nfs4_cb_ports)) != NULL) {
12340Sstevel@tonic-gate 		list_remove(&ncg->nfs4_cb_ports, p);
12350Sstevel@tonic-gate 		kmem_free(p, sizeof (*p));
12360Sstevel@tonic-gate 	}
12370Sstevel@tonic-gate 	list_destroy(&ncg->nfs4_cb_ports);
12380Sstevel@tonic-gate 	mutex_destroy(&ncg->nfs4_cb_lock);
12390Sstevel@tonic-gate 	list_destroy(&ncg->nfs4_dlist);
12400Sstevel@tonic-gate 	mutex_destroy(&ncg->nfs4_dlist_lock);
12410Sstevel@tonic-gate 	kmem_free(ncg, sizeof (*ncg));
12420Sstevel@tonic-gate }
12430Sstevel@tonic-gate 
12440Sstevel@tonic-gate void
nfs4_callback_init(void)12450Sstevel@tonic-gate nfs4_callback_init(void)
12460Sstevel@tonic-gate {
12470Sstevel@tonic-gate 	int i;
12480Sstevel@tonic-gate 	SVC_CALLOUT *nfs4_cb_sc;
12490Sstevel@tonic-gate 
12500Sstevel@tonic-gate 	/* initialize the callback table */
12510Sstevel@tonic-gate 	nfs4_cb_sc = kmem_alloc(nfs4_num_prognums *
12525151Swebaker 	    sizeof (SVC_CALLOUT), KM_SLEEP);
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 	for (i = 0; i < nfs4_num_prognums; i++) {
12550Sstevel@tonic-gate 		nfs4_cb_sc[i].sc_prog = NFS4_CALLBACK+i;
12560Sstevel@tonic-gate 		nfs4_cb_sc[i].sc_versmin = NFS_CB;
12570Sstevel@tonic-gate 		nfs4_cb_sc[i].sc_versmax = NFS_CB;
12580Sstevel@tonic-gate 		nfs4_cb_sc[i].sc_dispatch = cb_dispatch;
12590Sstevel@tonic-gate 	}
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 	nfs4_cb_sct.sct_size = nfs4_num_prognums;
12620Sstevel@tonic-gate 	nfs4_cb_sct.sct_free = FALSE;
12630Sstevel@tonic-gate 	nfs4_cb_sct.sct_sc = nfs4_cb_sc;
12640Sstevel@tonic-gate 
12650Sstevel@tonic-gate 	/*
12660Sstevel@tonic-gate 	 * Compute max bytes required for dyamically allocated parts
12670Sstevel@tonic-gate 	 * of cb_getattr reply.  Only size and change are supported now.
12680Sstevel@tonic-gate 	 * If CB_GETATTR is changed to reply with additional attrs,
12690Sstevel@tonic-gate 	 * additional sizes must be added below.
12700Sstevel@tonic-gate 	 *
12710Sstevel@tonic-gate 	 * fattr4_change + fattr4_size == uint64_t + uint64_t
12720Sstevel@tonic-gate 	 */
12730Sstevel@tonic-gate 	cb_getattr_bytes = 2 * BYTES_PER_XDR_UNIT + 2 * BYTES_PER_XDR_UNIT;
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 	zone_key_create(&nfs4_callback_zone_key, nfs4_callback_init_zone,
12760Sstevel@tonic-gate 	    nfs4_callback_shutdown_zone, nfs4_callback_fini_zone);
12770Sstevel@tonic-gate }
12780Sstevel@tonic-gate 
12790Sstevel@tonic-gate void
nfs4_callback_fini(void)12800Sstevel@tonic-gate nfs4_callback_fini(void)
12810Sstevel@tonic-gate {
12820Sstevel@tonic-gate }
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate /*
12850Sstevel@tonic-gate  * NB: This function can be called from the *wrong* zone (ie, the zone that
12860Sstevel@tonic-gate  * 'rp' belongs to and the caller's zone may not be the same).  This can happen
12870Sstevel@tonic-gate  * if the zone is going away and we get called from nfs4_async_inactive().  In
12880Sstevel@tonic-gate  * this case the globals will be NULL and we won't update the counters, which
12890Sstevel@tonic-gate  * doesn't matter as the zone is going away anyhow.
12900Sstevel@tonic-gate  */
12910Sstevel@tonic-gate static void
nfs4delegreturn_cleanup_impl(rnode4_t * rp,nfs4_server_t * np,struct nfs4_callback_globals * ncg)12920Sstevel@tonic-gate nfs4delegreturn_cleanup_impl(rnode4_t *rp, nfs4_server_t *np,
12930Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg)
12940Sstevel@tonic-gate {
12950Sstevel@tonic-gate 	mntinfo4_t *mi = VTOMI4(RTOV4(rp));
12960Sstevel@tonic-gate 	boolean_t need_rele = B_FALSE;
12970Sstevel@tonic-gate 
12986090Spf199842 	/*
12996090Spf199842 	 * Caller must be holding mi_recovlock in read mode
13006090Spf199842 	 * to call here.  This is provided by start_op.
13016090Spf199842 	 * Delegation management requires to grab s_lock
13026090Spf199842 	 * first and then r_statev4_lock.
13036090Spf199842 	 */
13046090Spf199842 
13056090Spf199842 	if (np == NULL) {
13066090Spf199842 		np = find_nfs4_server_all(mi, 1);
130710532SPavel.Filipensky@Sun.COM 		if (np == NULL)
130810532SPavel.Filipensky@Sun.COM 			return;
13096090Spf199842 		need_rele = B_TRUE;
13106090Spf199842 	} else {
13116090Spf199842 		mutex_enter(&np->s_lock);
13126090Spf199842 	}
13136090Spf199842 
13140Sstevel@tonic-gate 	mutex_enter(&rp->r_statev4_lock);
13150Sstevel@tonic-gate 
131610532SPavel.Filipensky@Sun.COM 	if (rp->r_deleg_type == OPEN_DELEGATE_NONE) {
131710532SPavel.Filipensky@Sun.COM 		mutex_exit(&rp->r_statev4_lock);
131810532SPavel.Filipensky@Sun.COM 		mutex_exit(&np->s_lock);
131910532SPavel.Filipensky@Sun.COM 		if (need_rele)
132010532SPavel.Filipensky@Sun.COM 			nfs4_server_rele(np);
132110532SPavel.Filipensky@Sun.COM 		return;
132210532SPavel.Filipensky@Sun.COM 	}
132310532SPavel.Filipensky@Sun.COM 
13240Sstevel@tonic-gate 	/*
13250Sstevel@tonic-gate 	 * Free the cred originally held when
13260Sstevel@tonic-gate 	 * the delegation was granted.  Caller must
13270Sstevel@tonic-gate 	 * hold this cred if it wants to use it after
13280Sstevel@tonic-gate 	 * this call.
13290Sstevel@tonic-gate 	 */
13300Sstevel@tonic-gate 	crfree(rp->r_deleg_cred);
13310Sstevel@tonic-gate 	rp->r_deleg_cred = NULL;
13320Sstevel@tonic-gate 	rp->r_deleg_type = OPEN_DELEGATE_NONE;
13330Sstevel@tonic-gate 	rp->r_deleg_needs_recovery = OPEN_DELEGATE_NONE;
13340Sstevel@tonic-gate 	rp->r_deleg_needs_recall = FALSE;
13350Sstevel@tonic-gate 	rp->r_deleg_return_pending = FALSE;
13360Sstevel@tonic-gate 
13370Sstevel@tonic-gate 	/*
13380Sstevel@tonic-gate 	 * Remove the rnode from the server's list and
13390Sstevel@tonic-gate 	 * update the ref counts.
13400Sstevel@tonic-gate 	 */
13410Sstevel@tonic-gate 	list_remove(&np->s_deleg_list, rp);
13426090Spf199842 	mutex_exit(&rp->r_statev4_lock);
13430Sstevel@tonic-gate 	nfs4_dec_state_ref_count_nolock(np, mi);
13440Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
13450Sstevel@tonic-gate 	/* removed list node removes a reference */
13460Sstevel@tonic-gate 	nfs4_server_rele(np);
13470Sstevel@tonic-gate 	if (need_rele)
13480Sstevel@tonic-gate 		nfs4_server_rele(np);
13490Sstevel@tonic-gate 	if (ncg != NULL)
13500Sstevel@tonic-gate 		ncg->nfs4_callback_stats.delegations.value.ui64--;
13510Sstevel@tonic-gate }
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate void
nfs4delegreturn_cleanup(rnode4_t * rp,nfs4_server_t * np)13540Sstevel@tonic-gate nfs4delegreturn_cleanup(rnode4_t *rp, nfs4_server_t *np)
13550Sstevel@tonic-gate {
13560Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
13570Sstevel@tonic-gate 
13580Sstevel@tonic-gate 	if (np != NULL) {
13590Sstevel@tonic-gate 		ncg = np->zone_globals;
1360766Scarlsonj 	} else if (nfs_zone() == VTOMI4(RTOV4(rp))->mi_zone) {
1361766Scarlsonj 		ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone());
13620Sstevel@tonic-gate 		ASSERT(ncg != NULL);
13630Sstevel@tonic-gate 	} else {
13640Sstevel@tonic-gate 		/*
13650Sstevel@tonic-gate 		 * Request coming from the wrong zone.
13660Sstevel@tonic-gate 		 */
13670Sstevel@tonic-gate 		ASSERT(getzoneid() == GLOBAL_ZONEID);
13680Sstevel@tonic-gate 		ncg = NULL;
13690Sstevel@tonic-gate 	}
13700Sstevel@tonic-gate 
13710Sstevel@tonic-gate 	nfs4delegreturn_cleanup_impl(rp, np, ncg);
13720Sstevel@tonic-gate }
13730Sstevel@tonic-gate 
13740Sstevel@tonic-gate static void
nfs4delegreturn_save_lost_rqst(int error,nfs4_lost_rqst_t * lost_rqstp,cred_t * cr,vnode_t * vp)13750Sstevel@tonic-gate nfs4delegreturn_save_lost_rqst(int error, nfs4_lost_rqst_t *lost_rqstp,
13760Sstevel@tonic-gate 	cred_t *cr, vnode_t *vp)
13770Sstevel@tonic-gate {
13780Sstevel@tonic-gate 	if (error != ETIMEDOUT && error != EINTR &&
13790Sstevel@tonic-gate 	    !NFS4_FRC_UNMT_ERR(error, vp->v_vfsp)) {
13800Sstevel@tonic-gate 		lost_rqstp->lr_op = 0;
13810Sstevel@tonic-gate 		return;
13820Sstevel@tonic-gate 	}
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_lost_rqst_debug, (CE_NOTE,
13855151Swebaker 	    "nfs4close_save_lost_rqst: error %d", error));
13860Sstevel@tonic-gate 
13870Sstevel@tonic-gate 	lost_rqstp->lr_op = OP_DELEGRETURN;
13880Sstevel@tonic-gate 	/*
13890Sstevel@tonic-gate 	 * The vp is held and rele'd via the recovery code.
13900Sstevel@tonic-gate 	 * See nfs4_save_lost_rqst.
13910Sstevel@tonic-gate 	 */
13920Sstevel@tonic-gate 	lost_rqstp->lr_vp = vp;
13930Sstevel@tonic-gate 	lost_rqstp->lr_dvp = NULL;
13940Sstevel@tonic-gate 	lost_rqstp->lr_oop = NULL;
13950Sstevel@tonic-gate 	lost_rqstp->lr_osp = NULL;
13960Sstevel@tonic-gate 	lost_rqstp->lr_lop = NULL;
13970Sstevel@tonic-gate 	lost_rqstp->lr_cr = cr;
13980Sstevel@tonic-gate 	lost_rqstp->lr_flk = NULL;
13990Sstevel@tonic-gate 	lost_rqstp->lr_putfirst = FALSE;
14000Sstevel@tonic-gate }
14010Sstevel@tonic-gate 
14020Sstevel@tonic-gate static void
nfs4delegreturn_otw(rnode4_t * rp,cred_t * cr,nfs4_error_t * ep)14030Sstevel@tonic-gate nfs4delegreturn_otw(rnode4_t *rp, cred_t *cr, nfs4_error_t *ep)
14040Sstevel@tonic-gate {
14050Sstevel@tonic-gate 	COMPOUND4args_clnt args;
14060Sstevel@tonic-gate 	COMPOUND4res_clnt res;
14070Sstevel@tonic-gate 	nfs_argop4 argops[3];
14080Sstevel@tonic-gate 	nfs4_ga_res_t *garp = NULL;
14090Sstevel@tonic-gate 	hrtime_t t;
14100Sstevel@tonic-gate 	int numops;
14110Sstevel@tonic-gate 	int doqueue = 1;
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 	args.ctag = TAG_DELEGRETURN;
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 	numops = 3;		/* PUTFH, GETATTR, DELEGRETURN */
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate 	args.array = argops;
14180Sstevel@tonic-gate 	args.array_len = numops;
14190Sstevel@tonic-gate 
14200Sstevel@tonic-gate 	argops[0].argop = OP_CPUTFH;
14210Sstevel@tonic-gate 	argops[0].nfs_argop4_u.opcputfh.sfh = rp->r_fh;
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate 	argops[1].argop = OP_GETATTR;
14240Sstevel@tonic-gate 	argops[1].nfs_argop4_u.opgetattr.attr_request = NFS4_VATTR_MASK;
14250Sstevel@tonic-gate 	argops[1].nfs_argop4_u.opgetattr.mi = VTOMI4(RTOV4(rp));
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 	argops[2].argop = OP_DELEGRETURN;
14280Sstevel@tonic-gate 	argops[2].nfs_argop4_u.opdelegreturn.deleg_stateid =
14295151Swebaker 	    rp->r_deleg_stateid;
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate 	t = gethrtime();
14320Sstevel@tonic-gate 	rfs4call(VTOMI4(RTOV4(rp)), &args, &res, cr, &doqueue, 0, ep);
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate 	if (ep->error)
14350Sstevel@tonic-gate 		return;
14360Sstevel@tonic-gate 
14370Sstevel@tonic-gate 	if (res.status == NFS4_OK) {
14380Sstevel@tonic-gate 		garp = &res.array[1].nfs_resop4_u.opgetattr.ga_res;
14390Sstevel@tonic-gate 		nfs4_attr_cache(RTOV4(rp), garp, t, cr, TRUE, NULL);
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 	}
14420Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
14430Sstevel@tonic-gate }
14440Sstevel@tonic-gate 
14450Sstevel@tonic-gate int
nfs4_do_delegreturn(rnode4_t * rp,int flags,cred_t * cr,struct nfs4_callback_globals * ncg)14460Sstevel@tonic-gate nfs4_do_delegreturn(rnode4_t *rp, int flags, cred_t *cr,
14470Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg)
14480Sstevel@tonic-gate {
14490Sstevel@tonic-gate 	vnode_t *vp = RTOV4(rp);
14500Sstevel@tonic-gate 	mntinfo4_t *mi = VTOMI4(vp);
14510Sstevel@tonic-gate 	nfs4_lost_rqst_t lost_rqst;
14520Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
14530Sstevel@tonic-gate 	bool_t needrecov = FALSE, recovonly, done = FALSE;
14540Sstevel@tonic-gate 	nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate 	ncg->nfs4_callback_stats.delegreturn.value.ui64++;
14570Sstevel@tonic-gate 
14580Sstevel@tonic-gate 	while (!done) {
14590Sstevel@tonic-gate 		e.error = nfs4_start_fop(mi, vp, NULL, OH_DELEGRETURN,
14605151Swebaker 		    &recov_state, &recovonly);
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 		if (e.error) {
14630Sstevel@tonic-gate 			if (flags & NFS4_DR_FORCE) {
14640Sstevel@tonic-gate 				(void) nfs_rw_enter_sig(&mi->mi_recovlock,
14650Sstevel@tonic-gate 				    RW_READER, 0);
14660Sstevel@tonic-gate 				nfs4delegreturn_cleanup_impl(rp, NULL, ncg);
14670Sstevel@tonic-gate 				nfs_rw_exit(&mi->mi_recovlock);
14680Sstevel@tonic-gate 			}
14690Sstevel@tonic-gate 			break;
14700Sstevel@tonic-gate 		}
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 		/*
14730Sstevel@tonic-gate 		 * Check to see if the delegation has already been
14740Sstevel@tonic-gate 		 * returned by the recovery thread.   The state of
14750Sstevel@tonic-gate 		 * the delegation cannot change at this point due
14760Sstevel@tonic-gate 		 * to start_fop and the r_deleg_recall_lock.
14770Sstevel@tonic-gate 		 */
14780Sstevel@tonic-gate 		if (rp->r_deleg_type == OPEN_DELEGATE_NONE) {
14790Sstevel@tonic-gate 			e.error = 0;
14800Sstevel@tonic-gate 			nfs4_end_op(mi, vp, NULL, &recov_state, needrecov);
14810Sstevel@tonic-gate 			break;
14820Sstevel@tonic-gate 		}
14830Sstevel@tonic-gate 
14840Sstevel@tonic-gate 		if (recovonly) {
14850Sstevel@tonic-gate 			/*
14860Sstevel@tonic-gate 			 * Delegation will be returned via the
14870Sstevel@tonic-gate 			 * recovery framework.  Build a lost request
14880Sstevel@tonic-gate 			 * structure, start recovery and get out.
14890Sstevel@tonic-gate 			 */
14900Sstevel@tonic-gate 			nfs4_error_init(&e, EINTR);
14910Sstevel@tonic-gate 			nfs4delegreturn_save_lost_rqst(e.error, &lost_rqst,
14925151Swebaker 			    cr, vp);
14930Sstevel@tonic-gate 			(void) nfs4_start_recovery(&e, mi, vp,
14945151Swebaker 			    NULL, &rp->r_deleg_stateid,
14955151Swebaker 			    lost_rqst.lr_op == OP_DELEGRETURN ?
1496*11291SRobert.Thurlow@Sun.COM 			    &lost_rqst : NULL, OP_DELEGRETURN, NULL,
1497*11291SRobert.Thurlow@Sun.COM 			    NULL, NULL);
14980Sstevel@tonic-gate 			nfs4_end_op(mi, vp, NULL, &recov_state, needrecov);
14990Sstevel@tonic-gate 			break;
15000Sstevel@tonic-gate 		}
15010Sstevel@tonic-gate 
15020Sstevel@tonic-gate 		nfs4delegreturn_otw(rp, cr, &e);
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate 		/*
15050Sstevel@tonic-gate 		 * Ignore some errors on delegreturn; no point in marking
15060Sstevel@tonic-gate 		 * the file dead on a state destroying operation.
15070Sstevel@tonic-gate 		 */
15080Sstevel@tonic-gate 		if (e.error == 0 && (nfs4_recov_marks_dead(e.stat) ||
15090Sstevel@tonic-gate 		    e.stat == NFS4ERR_BADHANDLE ||
15100Sstevel@tonic-gate 		    e.stat == NFS4ERR_STALE))
15110Sstevel@tonic-gate 			needrecov = FALSE;
15120Sstevel@tonic-gate 		else
15130Sstevel@tonic-gate 			needrecov = nfs4_needs_recovery(&e, TRUE, vp->v_vfsp);
15140Sstevel@tonic-gate 
15150Sstevel@tonic-gate 		if (needrecov) {
15160Sstevel@tonic-gate 			nfs4delegreturn_save_lost_rqst(e.error, &lost_rqst,
15175151Swebaker 			    cr, vp);
15180Sstevel@tonic-gate 			(void) nfs4_start_recovery(&e, mi, vp,
15195151Swebaker 			    NULL, &rp->r_deleg_stateid,
15205151Swebaker 			    lost_rqst.lr_op == OP_DELEGRETURN ?
1521*11291SRobert.Thurlow@Sun.COM 			    &lost_rqst : NULL, OP_DELEGRETURN, NULL,
1522*11291SRobert.Thurlow@Sun.COM 			    NULL, NULL);
15230Sstevel@tonic-gate 		} else {
15240Sstevel@tonic-gate 			nfs4delegreturn_cleanup_impl(rp, NULL, ncg);
15250Sstevel@tonic-gate 			done = TRUE;
15260Sstevel@tonic-gate 		}
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate 		nfs4_end_op(mi, vp, NULL, &recov_state, needrecov);
15290Sstevel@tonic-gate 	}
15300Sstevel@tonic-gate 	return (e.error);
15310Sstevel@tonic-gate }
15320Sstevel@tonic-gate 
15330Sstevel@tonic-gate /*
15340Sstevel@tonic-gate  * nfs4_resend_delegreturn - used to drive the delegreturn
15350Sstevel@tonic-gate  * operation via the recovery thread.
15360Sstevel@tonic-gate  */
15370Sstevel@tonic-gate void
nfs4_resend_delegreturn(nfs4_lost_rqst_t * lorp,nfs4_error_t * ep,nfs4_server_t * np)15380Sstevel@tonic-gate nfs4_resend_delegreturn(nfs4_lost_rqst_t *lorp, nfs4_error_t *ep,
15390Sstevel@tonic-gate 	nfs4_server_t *np)
15400Sstevel@tonic-gate {
15410Sstevel@tonic-gate 	rnode4_t *rp = VTOR4(lorp->lr_vp);
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate 	/* If the file failed recovery, just quit. */
15440Sstevel@tonic-gate 	mutex_enter(&rp->r_statelock);
15450Sstevel@tonic-gate 	if (rp->r_flags & R4RECOVERR) {
15460Sstevel@tonic-gate 		ep->error = EIO;
15470Sstevel@tonic-gate 	}
15480Sstevel@tonic-gate 	mutex_exit(&rp->r_statelock);
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate 	if (!ep->error)
15510Sstevel@tonic-gate 		nfs4delegreturn_otw(rp, lorp->lr_cr, ep);
15520Sstevel@tonic-gate 
15530Sstevel@tonic-gate 	/*
15540Sstevel@tonic-gate 	 * If recovery is now needed, then return the error
15550Sstevel@tonic-gate 	 * and status and let the recovery thread handle it,
15560Sstevel@tonic-gate 	 * including re-driving another delegreturn.  Otherwise,
15570Sstevel@tonic-gate 	 * just give up and clean up the delegation.
15580Sstevel@tonic-gate 	 */
15590Sstevel@tonic-gate 	if (nfs4_needs_recovery(ep, TRUE, lorp->lr_vp->v_vfsp))
15600Sstevel@tonic-gate 		return;
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate 	if (rp->r_deleg_type != OPEN_DELEGATE_NONE)
15630Sstevel@tonic-gate 		nfs4delegreturn_cleanup(rp, np);
15640Sstevel@tonic-gate 
15650Sstevel@tonic-gate 	nfs4_error_zinit(ep);
15660Sstevel@tonic-gate }
15670Sstevel@tonic-gate 
15680Sstevel@tonic-gate /*
15690Sstevel@tonic-gate  * nfs4delegreturn - general function to return a delegation.
15700Sstevel@tonic-gate  *
15710Sstevel@tonic-gate  * NFS4_DR_FORCE - return the delegation even if start_op fails
15720Sstevel@tonic-gate  * NFS4_DR_PUSH - push modified data back to the server via VOP_PUTPAGE
15730Sstevel@tonic-gate  * NFS4_DR_DISCARD - discard the delegation w/o delegreturn
15740Sstevel@tonic-gate  * NFS4_DR_DID_OP - calling function already did nfs4_start_op
15750Sstevel@tonic-gate  * NFS4_DR_RECALL - delegreturned initiated via CB_RECALL
15760Sstevel@tonic-gate  * NFS4_DR_REOPEN - do file reopens, if applicable
15770Sstevel@tonic-gate  */
15780Sstevel@tonic-gate static int
nfs4delegreturn_impl(rnode4_t * rp,int flags,struct nfs4_callback_globals * ncg)15790Sstevel@tonic-gate nfs4delegreturn_impl(rnode4_t *rp, int flags, struct nfs4_callback_globals *ncg)
15800Sstevel@tonic-gate {
15810Sstevel@tonic-gate 	int error = 0;
15820Sstevel@tonic-gate 	cred_t *cr = NULL;
15830Sstevel@tonic-gate 	vnode_t *vp;
15840Sstevel@tonic-gate 	bool_t needrecov = FALSE;
15850Sstevel@tonic-gate 	bool_t rw_entered = FALSE;
15860Sstevel@tonic-gate 	bool_t do_reopen;
15870Sstevel@tonic-gate 
15880Sstevel@tonic-gate 	vp = RTOV4(rp);
15890Sstevel@tonic-gate 
15900Sstevel@tonic-gate 	/*
15910Sstevel@tonic-gate 	 * If NFS4_DR_DISCARD is set by itself, take a short-cut and
15920Sstevel@tonic-gate 	 * discard without doing an otw DELEGRETURN.  This may only be used
15930Sstevel@tonic-gate 	 * by the recovery thread because it bypasses the synchronization
15940Sstevel@tonic-gate 	 * with r_deleg_recall_lock and mi->mi_recovlock.
15950Sstevel@tonic-gate 	 */
15960Sstevel@tonic-gate 	if (flags == NFS4_DR_DISCARD) {
15970Sstevel@tonic-gate 		nfs4delegreturn_cleanup_impl(rp, NULL, ncg);
15980Sstevel@tonic-gate 		return (0);
15990Sstevel@tonic-gate 	}
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate 	if (flags & NFS4_DR_DID_OP) {
16020Sstevel@tonic-gate 		/*
16030Sstevel@tonic-gate 		 * Caller had already done start_op, which means the
16040Sstevel@tonic-gate 		 * r_deleg_recall_lock is already held in READ mode
16050Sstevel@tonic-gate 		 * so we cannot take it in write mode.  Return the
16060Sstevel@tonic-gate 		 * delegation asynchronously.
16070Sstevel@tonic-gate 		 *
16080Sstevel@tonic-gate 		 * Remove the NFS4_DR_DID_OP flag so we don't
16090Sstevel@tonic-gate 		 * get stuck looping through here.
16100Sstevel@tonic-gate 		 */
16110Sstevel@tonic-gate 		VN_HOLD(vp);
16120Sstevel@tonic-gate 		nfs4delegreturn_async(rp, (flags & ~NFS4_DR_DID_OP), FALSE);
16130Sstevel@tonic-gate 		return (0);
16140Sstevel@tonic-gate 	}
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 	/*
16174033Sthurlow 	 * Verify we still have a delegation and crhold the credential.
16180Sstevel@tonic-gate 	 */
16194033Sthurlow 	mutex_enter(&rp->r_statev4_lock);
16204033Sthurlow 	if (rp->r_deleg_type == OPEN_DELEGATE_NONE) {
16214033Sthurlow 		mutex_exit(&rp->r_statev4_lock);
16220Sstevel@tonic-gate 		goto out;
16234033Sthurlow 	}
16240Sstevel@tonic-gate 	cr = rp->r_deleg_cred;
16254033Sthurlow 	ASSERT(cr != NULL);
16260Sstevel@tonic-gate 	crhold(cr);
16274033Sthurlow 	mutex_exit(&rp->r_statev4_lock);
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 	/*
16300Sstevel@tonic-gate 	 * Push the modified data back to the server synchronously
16310Sstevel@tonic-gate 	 * before doing DELEGRETURN.
16320Sstevel@tonic-gate 	 */
16330Sstevel@tonic-gate 	if (flags & NFS4_DR_PUSH)
16345331Samw 		(void) VOP_PUTPAGE(vp, 0, 0, 0, cr, NULL);
16350Sstevel@tonic-gate 
16360Sstevel@tonic-gate 	/*
16370Sstevel@tonic-gate 	 * Take r_deleg_recall_lock in WRITE mode, this will prevent
16380Sstevel@tonic-gate 	 * nfs4_is_otw_open_necessary from trying to use the delegation
16390Sstevel@tonic-gate 	 * while the DELEGRETURN is in progress.
16400Sstevel@tonic-gate 	 */
16410Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&rp->r_deleg_recall_lock, RW_WRITER, FALSE);
16420Sstevel@tonic-gate 
16430Sstevel@tonic-gate 	rw_entered = TRUE;
16440Sstevel@tonic-gate 
16450Sstevel@tonic-gate 	if (rp->r_deleg_type == OPEN_DELEGATE_NONE)
16460Sstevel@tonic-gate 		goto out;
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 	if (flags & NFS4_DR_REOPEN) {
16490Sstevel@tonic-gate 		/*
16500Sstevel@tonic-gate 		 * If R4RECOVERRP is already set, then skip re-opening
16510Sstevel@tonic-gate 		 * the delegation open streams and go straight to doing
16520Sstevel@tonic-gate 		 * delegreturn.  (XXX if the file has failed recovery, then the
16530Sstevel@tonic-gate 		 * delegreturn attempt is likely to be futile.)
16540Sstevel@tonic-gate 		 */
16550Sstevel@tonic-gate 		mutex_enter(&rp->r_statelock);
16560Sstevel@tonic-gate 		do_reopen = !(rp->r_flags & R4RECOVERRP);
16570Sstevel@tonic-gate 		mutex_exit(&rp->r_statelock);
16580Sstevel@tonic-gate 
16590Sstevel@tonic-gate 		if (do_reopen) {
16600Sstevel@tonic-gate 			error = deleg_reopen(vp, &needrecov, ncg, flags);
16610Sstevel@tonic-gate 			if (error != 0) {
16620Sstevel@tonic-gate 				if ((flags & (NFS4_DR_FORCE | NFS4_DR_RECALL))
16635151Swebaker 				    == 0)
16640Sstevel@tonic-gate 					goto out;
16650Sstevel@tonic-gate 			} else if (needrecov) {
16660Sstevel@tonic-gate 				if ((flags & NFS4_DR_FORCE) == 0)
16670Sstevel@tonic-gate 					goto out;
16680Sstevel@tonic-gate 			}
16690Sstevel@tonic-gate 		}
16700Sstevel@tonic-gate 	}
16710Sstevel@tonic-gate 
16720Sstevel@tonic-gate 	if (flags & NFS4_DR_DISCARD) {
16730Sstevel@tonic-gate 		mntinfo4_t *mi = VTOMI4(RTOV4(rp));
16740Sstevel@tonic-gate 
16750Sstevel@tonic-gate 		mutex_enter(&rp->r_statelock);
16760Sstevel@tonic-gate 		/*
16770Sstevel@tonic-gate 		 * deleg_return_pending is cleared inside of delegation_accept
16780Sstevel@tonic-gate 		 * when a delegation is accepted.  if this flag has been
16790Sstevel@tonic-gate 		 * cleared, then a new delegation has overwritten the one we
16800Sstevel@tonic-gate 		 * were about to throw away.
16810Sstevel@tonic-gate 		 */
16820Sstevel@tonic-gate 		if (!rp->r_deleg_return_pending) {
16830Sstevel@tonic-gate 			mutex_exit(&rp->r_statelock);
16840Sstevel@tonic-gate 			goto out;
16850Sstevel@tonic-gate 		}
16860Sstevel@tonic-gate 		mutex_exit(&rp->r_statelock);
16870Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, FALSE);
16880Sstevel@tonic-gate 		nfs4delegreturn_cleanup_impl(rp, NULL, ncg);
16890Sstevel@tonic-gate 		nfs_rw_exit(&mi->mi_recovlock);
16900Sstevel@tonic-gate 	} else {
16910Sstevel@tonic-gate 		error = nfs4_do_delegreturn(rp, flags, cr, ncg);
16920Sstevel@tonic-gate 	}
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate out:
16950Sstevel@tonic-gate 	if (cr)
16960Sstevel@tonic-gate 		crfree(cr);
16970Sstevel@tonic-gate 	if (rw_entered)
16980Sstevel@tonic-gate 		nfs_rw_exit(&rp->r_deleg_recall_lock);
16990Sstevel@tonic-gate 	return (error);
17000Sstevel@tonic-gate }
17010Sstevel@tonic-gate 
17020Sstevel@tonic-gate int
nfs4delegreturn(rnode4_t * rp,int flags)17030Sstevel@tonic-gate nfs4delegreturn(rnode4_t *rp, int flags)
17040Sstevel@tonic-gate {
17050Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
17060Sstevel@tonic-gate 
1707766Scarlsonj 	ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone());
17080Sstevel@tonic-gate 	ASSERT(ncg != NULL);
17090Sstevel@tonic-gate 
17100Sstevel@tonic-gate 	return (nfs4delegreturn_impl(rp, flags, ncg));
17110Sstevel@tonic-gate }
17120Sstevel@tonic-gate 
17130Sstevel@tonic-gate void
nfs4delegreturn_async(rnode4_t * rp,int flags,bool_t trunc)17140Sstevel@tonic-gate nfs4delegreturn_async(rnode4_t *rp, int flags, bool_t trunc)
17150Sstevel@tonic-gate {
17160Sstevel@tonic-gate 	struct cb_recall_pass *pp;
17170Sstevel@tonic-gate 
17180Sstevel@tonic-gate 	pp = kmem_alloc(sizeof (struct cb_recall_pass), KM_SLEEP);
17190Sstevel@tonic-gate 	pp->rp = rp;
17200Sstevel@tonic-gate 	pp->flags = flags;
17210Sstevel@tonic-gate 	pp->truncate = trunc;
17220Sstevel@tonic-gate 
17230Sstevel@tonic-gate 	/*
17240Sstevel@tonic-gate 	 * Fire up a thread to do the actual delegreturn
17250Sstevel@tonic-gate 	 * Caller must guarantee that the rnode doesn't
17260Sstevel@tonic-gate 	 * vanish (by calling VN_HOLD).
17270Sstevel@tonic-gate 	 */
17280Sstevel@tonic-gate 
17290Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, nfs4delegreturn_thread, pp, 0,
17305151Swebaker 	    minclsyspri);
17310Sstevel@tonic-gate }
17320Sstevel@tonic-gate 
17330Sstevel@tonic-gate static void
delegreturn_all_thread(rpcprog_t * pp)17340Sstevel@tonic-gate delegreturn_all_thread(rpcprog_t *pp)
17350Sstevel@tonic-gate {
17360Sstevel@tonic-gate 	nfs4_server_t *np;
17370Sstevel@tonic-gate 	bool_t found = FALSE;
17380Sstevel@tonic-gate 	rpcprog_t prog;
17390Sstevel@tonic-gate 	rnode4_t *rp;
17400Sstevel@tonic-gate 	vnode_t *vp;
17410Sstevel@tonic-gate 	zoneid_t zoneid = getzoneid();
17420Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
17430Sstevel@tonic-gate 
17440Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_drat_debug,
17455151Swebaker 	    (CE_NOTE, "delereturn_all_thread: prog %d\n", *pp));
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	prog = *pp;
17480Sstevel@tonic-gate 	kmem_free(pp, sizeof (*pp));
17490Sstevel@tonic-gate 	pp = NULL;
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
17520Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
17530Sstevel@tonic-gate 		if (np->zoneid == zoneid && np->s_program == prog) {
17540Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
17550Sstevel@tonic-gate 			found = TRUE;
17560Sstevel@tonic-gate 			break;
17570Sstevel@tonic-gate 		}
17580Sstevel@tonic-gate 	}
17590Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
17600Sstevel@tonic-gate 
17610Sstevel@tonic-gate 	/*
17620Sstevel@tonic-gate 	 * It's possible that the nfs4_server which was using this
17630Sstevel@tonic-gate 	 * program number has vanished since this thread is async.
17640Sstevel@tonic-gate 	 * If so, just return.  Your work here is finished, my friend.
17650Sstevel@tonic-gate 	 */
17660Sstevel@tonic-gate 	if (!found)
17670Sstevel@tonic-gate 		goto out;
17680Sstevel@tonic-gate 
17690Sstevel@tonic-gate 	ncg = np->zone_globals;
17700Sstevel@tonic-gate 	while ((rp = list_head(&np->s_deleg_list)) != NULL) {
17710Sstevel@tonic-gate 		vp = RTOV4(rp);
17720Sstevel@tonic-gate 		VN_HOLD(vp);
17730Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
17740Sstevel@tonic-gate 		(void) nfs4delegreturn_impl(rp, NFS4_DR_PUSH|NFS4_DR_REOPEN,
17755151Swebaker 		    ncg);
17760Sstevel@tonic-gate 		VN_RELE(vp);
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate 		/* retake the s_lock for next trip through the loop */
17790Sstevel@tonic-gate 		mutex_enter(&np->s_lock);
17800Sstevel@tonic-gate 	}
17810Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
17820Sstevel@tonic-gate out:
17830Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_drat_debug,
17845151Swebaker 	    (CE_NOTE, "delereturn_all_thread: complete\n"));
17850Sstevel@tonic-gate 	zthread_exit();
17860Sstevel@tonic-gate }
17870Sstevel@tonic-gate 
17880Sstevel@tonic-gate void
nfs4_delegreturn_all(nfs4_server_t * sp)17890Sstevel@tonic-gate nfs4_delegreturn_all(nfs4_server_t *sp)
17900Sstevel@tonic-gate {
17910Sstevel@tonic-gate 	rpcprog_t pro, *pp;
17920Sstevel@tonic-gate 
17930Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate 	/* Check to see if the delegation list is empty */
17960Sstevel@tonic-gate 
17970Sstevel@tonic-gate 	if (list_head(&sp->s_deleg_list) == NULL) {
17980Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
17990Sstevel@tonic-gate 		return;
18000Sstevel@tonic-gate 	}
18010Sstevel@tonic-gate 	/*
18020Sstevel@tonic-gate 	 * Grab the program number; the async thread will use this
18030Sstevel@tonic-gate 	 * to find the nfs4_server.
18040Sstevel@tonic-gate 	 */
18050Sstevel@tonic-gate 	pro = sp->s_program;
18060Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
18070Sstevel@tonic-gate 	pp = kmem_alloc(sizeof (rpcprog_t), KM_SLEEP);
18080Sstevel@tonic-gate 	*pp = pro;
18090Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, delegreturn_all_thread, pp, 0,
18100Sstevel@tonic-gate 	    minclsyspri);
18110Sstevel@tonic-gate }
18120Sstevel@tonic-gate 
18130Sstevel@tonic-gate 
18140Sstevel@tonic-gate /*
18150Sstevel@tonic-gate  * Discard any delegations
18160Sstevel@tonic-gate  *
18170Sstevel@tonic-gate  * Iterate over the servers s_deleg_list and
18180Sstevel@tonic-gate  * for matching mount-point rnodes discard
18190Sstevel@tonic-gate  * the delegation.
18200Sstevel@tonic-gate  */
18210Sstevel@tonic-gate void
nfs4_deleg_discard(mntinfo4_t * mi,nfs4_server_t * sp)18220Sstevel@tonic-gate nfs4_deleg_discard(mntinfo4_t *mi, nfs4_server_t *sp)
18230Sstevel@tonic-gate {
18240Sstevel@tonic-gate 	rnode4_t *rp, *next;
18250Sstevel@tonic-gate 	mntinfo4_t *r_mi;
18260Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
18270Sstevel@tonic-gate 
18280Sstevel@tonic-gate 	ASSERT(mutex_owned(&sp->s_lock));
18290Sstevel@tonic-gate 	ncg = sp->zone_globals;
18300Sstevel@tonic-gate 
18310Sstevel@tonic-gate 	for (rp = list_head(&sp->s_deleg_list); rp != NULL; rp = next) {
18320Sstevel@tonic-gate 		r_mi = VTOMI4(RTOV4(rp));
18330Sstevel@tonic-gate 		next = list_next(&sp->s_deleg_list, rp);
18340Sstevel@tonic-gate 
18350Sstevel@tonic-gate 		if (r_mi != mi) {
18360Sstevel@tonic-gate 			/*
18370Sstevel@tonic-gate 			 * Skip if this rnode is in not on the
18380Sstevel@tonic-gate 			 * same mount-point
18390Sstevel@tonic-gate 			 */
18400Sstevel@tonic-gate 			continue;
18410Sstevel@tonic-gate 		}
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 		ASSERT(rp->r_deleg_type == OPEN_DELEGATE_READ);
18440Sstevel@tonic-gate 
18450Sstevel@tonic-gate #ifdef DEBUG
18460Sstevel@tonic-gate 		if (nfs4_client_recov_debug) {
18470Sstevel@tonic-gate 			zprintf(getzoneid(),
18480Sstevel@tonic-gate 			    "nfs4_deleg_discard: matched rnode %p "
18490Sstevel@tonic-gate 			"-- discarding delegation\n", (void *)rp);
18500Sstevel@tonic-gate 		}
18510Sstevel@tonic-gate #endif
18520Sstevel@tonic-gate 		mutex_enter(&rp->r_statev4_lock);
18530Sstevel@tonic-gate 		/*
18540Sstevel@tonic-gate 		 * Free the cred originally held when the delegation
18550Sstevel@tonic-gate 		 * was granted. Also need to decrement the refcnt
18560Sstevel@tonic-gate 		 * on this server for each delegation we discard
18570Sstevel@tonic-gate 		 */
18580Sstevel@tonic-gate 		if (rp->r_deleg_cred)
18590Sstevel@tonic-gate 			crfree(rp->r_deleg_cred);
18600Sstevel@tonic-gate 		rp->r_deleg_cred = NULL;
18610Sstevel@tonic-gate 		rp->r_deleg_type = OPEN_DELEGATE_NONE;
18620Sstevel@tonic-gate 		rp->r_deleg_needs_recovery = OPEN_DELEGATE_NONE;
18630Sstevel@tonic-gate 		rp->r_deleg_needs_recall = FALSE;
18640Sstevel@tonic-gate 		ASSERT(sp->s_refcnt > 1);
18650Sstevel@tonic-gate 		sp->s_refcnt--;
18660Sstevel@tonic-gate 		list_remove(&sp->s_deleg_list, rp);
18670Sstevel@tonic-gate 		mutex_exit(&rp->r_statev4_lock);
18680Sstevel@tonic-gate 		nfs4_dec_state_ref_count_nolock(sp, mi);
18690Sstevel@tonic-gate 		ncg->nfs4_callback_stats.delegations.value.ui64--;
18700Sstevel@tonic-gate 	}
18710Sstevel@tonic-gate }
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate /*
18740Sstevel@tonic-gate  * Reopen any open streams that were covered by the given file's
18750Sstevel@tonic-gate  * delegation.
18760Sstevel@tonic-gate  * Returns zero or an errno value.  If there was no error, *recovp
18770Sstevel@tonic-gate  * indicates whether recovery was initiated.
18780Sstevel@tonic-gate  */
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate static int
deleg_reopen(vnode_t * vp,bool_t * recovp,struct nfs4_callback_globals * ncg,int flags)18810Sstevel@tonic-gate deleg_reopen(vnode_t *vp, bool_t *recovp, struct nfs4_callback_globals *ncg,
18820Sstevel@tonic-gate 	int flags)
18830Sstevel@tonic-gate {
18840Sstevel@tonic-gate 	nfs4_open_stream_t *osp;
18850Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
18860Sstevel@tonic-gate 	bool_t needrecov = FALSE;
18870Sstevel@tonic-gate 	mntinfo4_t *mi;
18880Sstevel@tonic-gate 	rnode4_t *rp;
18890Sstevel@tonic-gate 	nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
18900Sstevel@tonic-gate 	int claimnull;
18910Sstevel@tonic-gate 
18920Sstevel@tonic-gate 	mi = VTOMI4(vp);
18930Sstevel@tonic-gate 	rp = VTOR4(vp);
18940Sstevel@tonic-gate 
18950Sstevel@tonic-gate 	recov_state.rs_flags = 0;
18960Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
18970Sstevel@tonic-gate 
18980Sstevel@tonic-gate retry:
18990Sstevel@tonic-gate 	if ((e.error = nfs4_start_op(mi, vp, NULL, &recov_state)) != 0) {
19000Sstevel@tonic-gate 		return (e.error);
19010Sstevel@tonic-gate 	}
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate 	/*
19040Sstevel@tonic-gate 	 * if we mean to discard the delegation, it must be BAD, so don't
19050Sstevel@tonic-gate 	 * use it when doing the reopen or it will fail too.
19060Sstevel@tonic-gate 	 */
19070Sstevel@tonic-gate 	claimnull = (flags & NFS4_DR_DISCARD);
19080Sstevel@tonic-gate 	/*
19090Sstevel@tonic-gate 	 * Loop through the open streams for this rnode to find
19100Sstevel@tonic-gate 	 * all of the ones created using the delegation state ID.
19110Sstevel@tonic-gate 	 * Each of these needs to be re-opened.
19120Sstevel@tonic-gate 	 */
19130Sstevel@tonic-gate 
19140Sstevel@tonic-gate 	while ((osp = get_next_deleg_stream(rp, claimnull)) != NULL) {
19150Sstevel@tonic-gate 
19160Sstevel@tonic-gate 		if (claimnull) {
19170Sstevel@tonic-gate 			nfs4_reopen(vp, osp, &e, CLAIM_NULL, FALSE, FALSE);
19180Sstevel@tonic-gate 		} else {
19190Sstevel@tonic-gate 			ncg->nfs4_callback_stats.claim_cur.value.ui64++;
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate 			nfs4_reopen(vp, osp, &e, CLAIM_DELEGATE_CUR, FALSE,
19225151Swebaker 			    FALSE);
19230Sstevel@tonic-gate 			if (e.error == 0 && e.stat == NFS4_OK)
19240Sstevel@tonic-gate 				ncg->nfs4_callback_stats.
19255151Swebaker 				    claim_cur_ok.value.ui64++;
19260Sstevel@tonic-gate 		}
19270Sstevel@tonic-gate 
19280Sstevel@tonic-gate 		if (e.error == EAGAIN) {
19290Sstevel@tonic-gate 			nfs4_end_op(mi, vp, NULL, &recov_state, TRUE);
19300Sstevel@tonic-gate 			goto retry;
19310Sstevel@tonic-gate 		}
19320Sstevel@tonic-gate 
19330Sstevel@tonic-gate 		/*
19340Sstevel@tonic-gate 		 * if error is EINTR, ETIMEDOUT, or NFS4_FRC_UNMT_ERR, then
19350Sstevel@tonic-gate 		 * recovery has already been started inside of nfs4_reopen.
19360Sstevel@tonic-gate 		 */
19370Sstevel@tonic-gate 		if (e.error == EINTR || e.error == ETIMEDOUT ||
19380Sstevel@tonic-gate 		    NFS4_FRC_UNMT_ERR(e.error, vp->v_vfsp)) {
19390Sstevel@tonic-gate 			open_stream_rele(osp, rp);
19400Sstevel@tonic-gate 			break;
19410Sstevel@tonic-gate 		}
19420Sstevel@tonic-gate 
19430Sstevel@tonic-gate 		needrecov = nfs4_needs_recovery(&e, TRUE, vp->v_vfsp);
19440Sstevel@tonic-gate 
19450Sstevel@tonic-gate 		if (e.error != 0 && !needrecov) {
19460Sstevel@tonic-gate 			/*
19470Sstevel@tonic-gate 			 * Recovery is not possible, but don't give up yet;
19480Sstevel@tonic-gate 			 * we'd still like to do delegreturn after
19490Sstevel@tonic-gate 			 * reopening as many streams as possible.
19500Sstevel@tonic-gate 			 * Continue processing the open streams.
19510Sstevel@tonic-gate 			 */
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate 			ncg->nfs4_callback_stats.recall_failed.value.ui64++;
19540Sstevel@tonic-gate 
19550Sstevel@tonic-gate 		} else if (needrecov) {
19560Sstevel@tonic-gate 			/*
19570Sstevel@tonic-gate 			 * Start recovery and bail out.  The recovery
19580Sstevel@tonic-gate 			 * thread will take it from here.
19590Sstevel@tonic-gate 			 */
19600Sstevel@tonic-gate 			(void) nfs4_start_recovery(&e, mi, vp, NULL, NULL,
1961*11291SRobert.Thurlow@Sun.COM 			    NULL, OP_OPEN, NULL, NULL, NULL);
19620Sstevel@tonic-gate 			open_stream_rele(osp, rp);
19630Sstevel@tonic-gate 			*recovp = TRUE;
19640Sstevel@tonic-gate 			break;
19650Sstevel@tonic-gate 		}
19660Sstevel@tonic-gate 
19670Sstevel@tonic-gate 		open_stream_rele(osp, rp);
19680Sstevel@tonic-gate 	}
19690Sstevel@tonic-gate 
19700Sstevel@tonic-gate 	nfs4_end_op(mi, vp, NULL, &recov_state, needrecov);
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	return (e.error);
19730Sstevel@tonic-gate }
19740Sstevel@tonic-gate 
19750Sstevel@tonic-gate /*
19760Sstevel@tonic-gate  * get_next_deleg_stream - returns the next open stream which
19770Sstevel@tonic-gate  * represents a delegation for this rnode.  In order to assure
19780Sstevel@tonic-gate  * forward progress, the caller must guarantee that each open
19790Sstevel@tonic-gate  * stream returned is changed so that a future call won't return
19800Sstevel@tonic-gate  * it again.
19810Sstevel@tonic-gate  *
19820Sstevel@tonic-gate  * There are several ways for the open stream to change.  If the open
19830Sstevel@tonic-gate  * stream is !os_delegation, then we aren't interested in it.  Also, if
19840Sstevel@tonic-gate  * either os_failed_reopen or !os_valid, then don't return the osp.
19850Sstevel@tonic-gate  *
19860Sstevel@tonic-gate  * If claimnull is false (doing reopen CLAIM_DELEGATE_CUR) then return
19870Sstevel@tonic-gate  * the osp if it is an os_delegation open stream.  Also, if the rnode still
19880Sstevel@tonic-gate  * has r_deleg_return_pending, then return the os_delegation osp.  Lastly,
19890Sstevel@tonic-gate  * if the rnode's r_deleg_stateid is different from the osp's open_stateid,
19900Sstevel@tonic-gate  * then return the osp.
19910Sstevel@tonic-gate  *
19920Sstevel@tonic-gate  * We have already taken the 'r_deleg_recall_lock' as WRITER, which
19930Sstevel@tonic-gate  * prevents new OPENs from going OTW (as start_fop takes this
19940Sstevel@tonic-gate  * lock in READ mode); thus, no new open streams can be created
19955331Samw  * (which inherently means no new delegation open streams are
19960Sstevel@tonic-gate  * being created).
19970Sstevel@tonic-gate  */
19980Sstevel@tonic-gate 
19990Sstevel@tonic-gate static nfs4_open_stream_t *
get_next_deleg_stream(rnode4_t * rp,int claimnull)20000Sstevel@tonic-gate get_next_deleg_stream(rnode4_t *rp, int claimnull)
20010Sstevel@tonic-gate {
20020Sstevel@tonic-gate 	nfs4_open_stream_t	*osp;
20030Sstevel@tonic-gate 
20040Sstevel@tonic-gate 	ASSERT(nfs_rw_lock_held(&rp->r_deleg_recall_lock, RW_WRITER));
20050Sstevel@tonic-gate 
20060Sstevel@tonic-gate 	/*
20070Sstevel@tonic-gate 	 * Search through the list of open streams looking for
20080Sstevel@tonic-gate 	 * one that was created while holding the delegation.
20090Sstevel@tonic-gate 	 */
20100Sstevel@tonic-gate 	mutex_enter(&rp->r_os_lock);
20110Sstevel@tonic-gate 	for (osp = list_head(&rp->r_open_streams); osp != NULL;
20120Sstevel@tonic-gate 	    osp = list_next(&rp->r_open_streams, osp)) {
20130Sstevel@tonic-gate 		mutex_enter(&osp->os_sync_lock);
20140Sstevel@tonic-gate 		if (!osp->os_delegation || osp->os_failed_reopen ||
20150Sstevel@tonic-gate 		    !osp->os_valid) {
20160Sstevel@tonic-gate 			mutex_exit(&osp->os_sync_lock);
20170Sstevel@tonic-gate 			continue;
20180Sstevel@tonic-gate 		}
20190Sstevel@tonic-gate 		if (!claimnull || rp->r_deleg_return_pending ||
20200Sstevel@tonic-gate 		    !stateid4_cmp(&osp->open_stateid, &rp->r_deleg_stateid)) {
20210Sstevel@tonic-gate 			osp->os_ref_count++;
20220Sstevel@tonic-gate 			mutex_exit(&osp->os_sync_lock);
20230Sstevel@tonic-gate 			mutex_exit(&rp->r_os_lock);
20240Sstevel@tonic-gate 			return (osp);
20250Sstevel@tonic-gate 		}
20260Sstevel@tonic-gate 		mutex_exit(&osp->os_sync_lock);
20270Sstevel@tonic-gate 	}
20280Sstevel@tonic-gate 	mutex_exit(&rp->r_os_lock);
20290Sstevel@tonic-gate 
20300Sstevel@tonic-gate 	return (NULL);
20310Sstevel@tonic-gate }
20320Sstevel@tonic-gate 
20330Sstevel@tonic-gate static void
nfs4delegreturn_thread(struct cb_recall_pass * args)20340Sstevel@tonic-gate nfs4delegreturn_thread(struct cb_recall_pass *args)
20350Sstevel@tonic-gate {
20360Sstevel@tonic-gate 	rnode4_t *rp;
20370Sstevel@tonic-gate 	vnode_t *vp;
20380Sstevel@tonic-gate 	cred_t *cr;
20390Sstevel@tonic-gate 	int dtype, error, flags;
20400Sstevel@tonic-gate 	bool_t rdirty, rip;
20410Sstevel@tonic-gate 	kmutex_t cpr_lock;
20420Sstevel@tonic-gate 	callb_cpr_t cpr_info;
20430Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
20440Sstevel@tonic-gate 
2045766Scarlsonj 	ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone());
20460Sstevel@tonic-gate 	ASSERT(ncg != NULL);
20470Sstevel@tonic-gate 
20480Sstevel@tonic-gate 	mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL);
20490Sstevel@tonic-gate 
20500Sstevel@tonic-gate 	CALLB_CPR_INIT(&cpr_info, &cpr_lock, callb_generic_cpr,
20515151Swebaker 	    "nfsv4delegRtn");
20520Sstevel@tonic-gate 
20530Sstevel@tonic-gate 	rp = args->rp;
20540Sstevel@tonic-gate 	vp = RTOV4(rp);
20550Sstevel@tonic-gate 
20560Sstevel@tonic-gate 	mutex_enter(&rp->r_statev4_lock);
20570Sstevel@tonic-gate 	if (rp->r_deleg_type == OPEN_DELEGATE_NONE) {
20580Sstevel@tonic-gate 		mutex_exit(&rp->r_statev4_lock);
20590Sstevel@tonic-gate 		goto out;
20600Sstevel@tonic-gate 	}
20610Sstevel@tonic-gate 	mutex_exit(&rp->r_statev4_lock);
20620Sstevel@tonic-gate 
20630Sstevel@tonic-gate 	/*
20640Sstevel@tonic-gate 	 * Take the read-write lock in read mode to prevent other
20650Sstevel@tonic-gate 	 * threads from modifying the data during the recall.  This
20660Sstevel@tonic-gate 	 * doesn't affect mmappers.
20670Sstevel@tonic-gate 	 */
20680Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&rp->r_rwlock, RW_READER, FALSE);
20690Sstevel@tonic-gate 
20700Sstevel@tonic-gate 	/* Proceed with delegreturn */
20710Sstevel@tonic-gate 
20720Sstevel@tonic-gate 	mutex_enter(&rp->r_statev4_lock);
20730Sstevel@tonic-gate 	if (rp->r_deleg_type == OPEN_DELEGATE_NONE) {
20740Sstevel@tonic-gate 		mutex_exit(&rp->r_statev4_lock);
20750Sstevel@tonic-gate 		nfs_rw_exit(&rp->r_rwlock);
20760Sstevel@tonic-gate 		goto out;
20770Sstevel@tonic-gate 	}
20780Sstevel@tonic-gate 	dtype = rp->r_deleg_type;
20790Sstevel@tonic-gate 	cr = rp->r_deleg_cred;
20800Sstevel@tonic-gate 	ASSERT(cr != NULL);
20810Sstevel@tonic-gate 	crhold(cr);
20820Sstevel@tonic-gate 	mutex_exit(&rp->r_statev4_lock);
20830Sstevel@tonic-gate 
20840Sstevel@tonic-gate 	flags = args->flags;
20850Sstevel@tonic-gate 
20860Sstevel@tonic-gate 	/*
20870Sstevel@tonic-gate 	 * If the file is being truncated at the server, then throw
20880Sstevel@tonic-gate 	 * away all of the pages, it doesn't matter what flavor of
20890Sstevel@tonic-gate 	 * delegation we have.
20900Sstevel@tonic-gate 	 */
20910Sstevel@tonic-gate 
20920Sstevel@tonic-gate 	if (args->truncate) {
20930Sstevel@tonic-gate 		ncg->nfs4_callback_stats.recall_trunc.value.ui64++;
20940Sstevel@tonic-gate 		nfs4_invalidate_pages(vp, 0, cr);
20950Sstevel@tonic-gate 	} else if (dtype == OPEN_DELEGATE_WRITE) {
20960Sstevel@tonic-gate 
20970Sstevel@tonic-gate 		mutex_enter(&rp->r_statelock);
20980Sstevel@tonic-gate 		rdirty = rp->r_flags & R4DIRTY;
20990Sstevel@tonic-gate 		mutex_exit(&rp->r_statelock);
21000Sstevel@tonic-gate 
21010Sstevel@tonic-gate 		if (rdirty) {
21025331Samw 			error = VOP_PUTPAGE(vp, 0, 0, 0, cr, NULL);
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 			if (error)
21050Sstevel@tonic-gate 				CB_WARN1("nfs4delegreturn_thread:"
21060Sstevel@tonic-gate 				" VOP_PUTPAGE: %d\n", error);
21070Sstevel@tonic-gate 		}
21080Sstevel@tonic-gate 		/* turn off NFS4_DR_PUSH because we just did that above. */
21090Sstevel@tonic-gate 		flags &= ~NFS4_DR_PUSH;
21100Sstevel@tonic-gate 	}
21110Sstevel@tonic-gate 
21120Sstevel@tonic-gate 	mutex_enter(&rp->r_statelock);
21130Sstevel@tonic-gate 	rip =  rp->r_flags & R4RECOVERRP;
21140Sstevel@tonic-gate 	mutex_exit(&rp->r_statelock);
21150Sstevel@tonic-gate 
21160Sstevel@tonic-gate 	/* If a failed recovery is indicated, discard the pages */
21170Sstevel@tonic-gate 
21180Sstevel@tonic-gate 	if (rip) {
21190Sstevel@tonic-gate 
21205331Samw 		error = VOP_PUTPAGE(vp, 0, 0, B_INVAL, cr, NULL);
21210Sstevel@tonic-gate 
21220Sstevel@tonic-gate 		if (error)
21230Sstevel@tonic-gate 			CB_WARN1("nfs4delegreturn_thread: VOP_PUTPAGE: %d\n",
21245151Swebaker 			    error);
21250Sstevel@tonic-gate 	}
21260Sstevel@tonic-gate 
21270Sstevel@tonic-gate 	/*
21280Sstevel@tonic-gate 	 * Pass the flags to nfs4delegreturn_impl, but be sure not to pass
21290Sstevel@tonic-gate 	 * NFS4_DR_DID_OP, which just calls nfs4delegreturn_async again.
21300Sstevel@tonic-gate 	 */
21310Sstevel@tonic-gate 	flags &= ~NFS4_DR_DID_OP;
21320Sstevel@tonic-gate 
21330Sstevel@tonic-gate 	(void) nfs4delegreturn_impl(rp, flags, ncg);
21340Sstevel@tonic-gate 
21350Sstevel@tonic-gate 	nfs_rw_exit(&rp->r_rwlock);
21360Sstevel@tonic-gate 	crfree(cr);
21370Sstevel@tonic-gate out:
21380Sstevel@tonic-gate 	kmem_free(args, sizeof (struct cb_recall_pass));
21390Sstevel@tonic-gate 	VN_RELE(vp);
21400Sstevel@tonic-gate 	mutex_enter(&cpr_lock);
21410Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cpr_info);
21420Sstevel@tonic-gate 	mutex_destroy(&cpr_lock);
21430Sstevel@tonic-gate 	zthread_exit();
21440Sstevel@tonic-gate }
21450Sstevel@tonic-gate 
21460Sstevel@tonic-gate /*
21470Sstevel@tonic-gate  * This function has one assumption that the caller of this function is
21480Sstevel@tonic-gate  * either doing recovery (therefore cannot call nfs4_start_op) or has
21490Sstevel@tonic-gate  * already called nfs4_start_op().
21500Sstevel@tonic-gate  */
21510Sstevel@tonic-gate void
nfs4_delegation_accept(rnode4_t * rp,open_claim_type4 claim,OPEN4res * res,nfs4_ga_res_t * garp,cred_t * cr)21525151Swebaker nfs4_delegation_accept(rnode4_t *rp, open_claim_type4 claim, OPEN4res *res,
21530Sstevel@tonic-gate 	nfs4_ga_res_t *garp, cred_t *cr)
21540Sstevel@tonic-gate {
21550Sstevel@tonic-gate 	open_read_delegation4 *orp;
21560Sstevel@tonic-gate 	open_write_delegation4 *owp;
21570Sstevel@tonic-gate 	nfs4_server_t *np;
21580Sstevel@tonic-gate 	bool_t already = FALSE;
21590Sstevel@tonic-gate 	bool_t recall = FALSE;
21600Sstevel@tonic-gate 	bool_t valid_garp = TRUE;
21615151Swebaker 	bool_t delegation_granted = FALSE;
21625151Swebaker 	bool_t dr_needed = FALSE;
21635151Swebaker 	bool_t recov;
21645151Swebaker 	int dr_flags = 0;
21650Sstevel@tonic-gate 	long mapcnt;
21660Sstevel@tonic-gate 	uint_t rflag;
21670Sstevel@tonic-gate 	mntinfo4_t *mi;
21680Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
2169536Srmesta 	open_delegation_type4 odt;
21700Sstevel@tonic-gate 
2171766Scarlsonj 	ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone());
21720Sstevel@tonic-gate 	ASSERT(ncg != NULL);
21730Sstevel@tonic-gate 
21745151Swebaker 	mi = VTOMI4(RTOV4(rp));
21755151Swebaker 
21765151Swebaker 	/*
21775151Swebaker 	 * Accept a delegation granted to the client via an OPEN.
21785151Swebaker 	 * Set the delegation fields in the rnode and insert the
21795151Swebaker 	 * rnode onto the list anchored in the nfs4_server_t.  The
21805151Swebaker 	 * proper locking order requires the nfs4_server_t first,
21815151Swebaker 	 * even though it may not be needed in all cases.
21825151Swebaker 	 *
21835151Swebaker 	 * NB: find_nfs4_server returns with s_lock held.
21845151Swebaker 	 */
21855151Swebaker 
21865151Swebaker 	if ((np = find_nfs4_server(mi)) == NULL)
21875151Swebaker 		return;
21885151Swebaker 
21895151Swebaker 	/* grab the statelock too, for examining r_mapcnt */
21905151Swebaker 	mutex_enter(&rp->r_statelock);
21910Sstevel@tonic-gate 	mutex_enter(&rp->r_statev4_lock);
21920Sstevel@tonic-gate 
21930Sstevel@tonic-gate 	if (rp->r_deleg_type == OPEN_DELEGATE_READ ||
21940Sstevel@tonic-gate 	    rp->r_deleg_type == OPEN_DELEGATE_WRITE)
21950Sstevel@tonic-gate 		already = TRUE;
21960Sstevel@tonic-gate 
2197536Srmesta 	odt = res->delegation.delegation_type;
2198536Srmesta 
2199536Srmesta 	if (odt == OPEN_DELEGATE_READ) {
2200536Srmesta 
22010Sstevel@tonic-gate 		rp->r_deleg_type = res->delegation.delegation_type;
22020Sstevel@tonic-gate 		orp = &res->delegation.open_delegation4_u.read;
22030Sstevel@tonic-gate 		rp->r_deleg_stateid = orp->stateid;
22040Sstevel@tonic-gate 		rp->r_deleg_perms = orp->permissions;
22055151Swebaker 		if (claim == CLAIM_PREVIOUS)
22065151Swebaker 			if ((recall = orp->recall) != 0)
22075151Swebaker 				dr_needed = TRUE;
22085151Swebaker 
22095151Swebaker 		delegation_granted = TRUE;
22100Sstevel@tonic-gate 
22110Sstevel@tonic-gate 		ncg->nfs4_callback_stats.delegations.value.ui64++;
22120Sstevel@tonic-gate 		ncg->nfs4_callback_stats.delegaccept_r.value.ui64++;
22130Sstevel@tonic-gate 
2214536Srmesta 	} else if (odt == OPEN_DELEGATE_WRITE) {
2215536Srmesta 
22160Sstevel@tonic-gate 		rp->r_deleg_type = res->delegation.delegation_type;
22170Sstevel@tonic-gate 		owp = &res->delegation.open_delegation4_u.write;
22180Sstevel@tonic-gate 		rp->r_deleg_stateid = owp->stateid;
22190Sstevel@tonic-gate 		rp->r_deleg_perms = owp->permissions;
22200Sstevel@tonic-gate 		rp->r_deleg_limit = owp->space_limit;
22215151Swebaker 		if (claim == CLAIM_PREVIOUS)
22225151Swebaker 			if ((recall = owp->recall) != 0)
22235151Swebaker 				dr_needed = TRUE;
22245151Swebaker 
22255151Swebaker 		delegation_granted = TRUE;
22260Sstevel@tonic-gate 
22270Sstevel@tonic-gate 		if (garp == NULL || !garp->n4g_change_valid) {
22280Sstevel@tonic-gate 			valid_garp = FALSE;
22290Sstevel@tonic-gate 			rp->r_deleg_change = 0;
22300Sstevel@tonic-gate 			rp->r_deleg_change_grant = 0;
22310Sstevel@tonic-gate 		} else {
22320Sstevel@tonic-gate 			rp->r_deleg_change = garp->n4g_change;
22330Sstevel@tonic-gate 			rp->r_deleg_change_grant = garp->n4g_change;
22340Sstevel@tonic-gate 		}
22350Sstevel@tonic-gate 		mapcnt = rp->r_mapcnt;
22360Sstevel@tonic-gate 		rflag = rp->r_flags;
22370Sstevel@tonic-gate 
22380Sstevel@tonic-gate 		/*
22390Sstevel@tonic-gate 		 * Update the delegation change attribute if
22400Sstevel@tonic-gate 		 * there are mappers for the file is dirty.  This
22410Sstevel@tonic-gate 		 * might be the case during recovery after server
22420Sstevel@tonic-gate 		 * reboot.
22430Sstevel@tonic-gate 		 */
22440Sstevel@tonic-gate 		if (mapcnt > 0 || rflag & R4DIRTY)
22450Sstevel@tonic-gate 			rp->r_deleg_change++;
22460Sstevel@tonic-gate 
22470Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_callback_debug, (CE_NOTE,
22485151Swebaker 		    "nfs4_delegation_accept: r_deleg_change: 0x%x\n",
22495151Swebaker 		    (int)(rp->r_deleg_change >> 32)));
22500Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_callback_debug, (CE_NOTE,
22515151Swebaker 		    "nfs4_delegation_accept: r_delg_change_grant: 0x%x\n",
22525151Swebaker 		    (int)(rp->r_deleg_change_grant >> 32)));
22535151Swebaker 
2254536Srmesta 
2255536Srmesta 		ncg->nfs4_callback_stats.delegations.value.ui64++;
2256536Srmesta 		ncg->nfs4_callback_stats.delegaccept_rw.value.ui64++;
22575151Swebaker 	} else if (already) {
22585151Swebaker 		/*
22595151Swebaker 		 * No delegation granted.  If the rnode currently has
22605151Swebaker 		 * has one, then consider it tainted and return it.
22615151Swebaker 		 */
22625151Swebaker 		dr_needed = TRUE;
22635151Swebaker 	}
22645151Swebaker 
22655151Swebaker 	if (delegation_granted) {
22665151Swebaker 		/* Add the rnode to the list. */
22675151Swebaker 		if (!already) {
22685151Swebaker 			crhold(cr);
22695151Swebaker 			rp->r_deleg_cred = cr;
22705151Swebaker 
22715151Swebaker 			ASSERT(mutex_owned(&np->s_lock));
22725151Swebaker 			list_insert_head(&np->s_deleg_list, rp);
22735151Swebaker 			/* added list node gets a reference */
22745151Swebaker 			np->s_refcnt++;
22755151Swebaker 			nfs4_inc_state_ref_count_nolock(np, mi);
22760Sstevel@tonic-gate 		}
22775151Swebaker 		rp->r_deleg_needs_recovery = OPEN_DELEGATE_NONE;
22780Sstevel@tonic-gate 	}
22790Sstevel@tonic-gate 
22800Sstevel@tonic-gate 	/*
22815151Swebaker 	 * We've now safely accepted the delegation, if any.  Drop the
22825151Swebaker 	 * locks and figure out what post-processing is needed.  We'd
22835151Swebaker 	 * like to retain r_statev4_lock, but nfs4_server_rele takes
22845151Swebaker 	 * s_lock which would be a lock ordering violation.
22855151Swebaker 	 */
22865151Swebaker 	mutex_exit(&rp->r_statev4_lock);
22875151Swebaker 	mutex_exit(&rp->r_statelock);
22885151Swebaker 	mutex_exit(&np->s_lock);
22895151Swebaker 	nfs4_server_rele(np);
22905151Swebaker 
22915151Swebaker 	/*
22925151Swebaker 	 * Check to see if we are in recovery.  Remember that
22935151Swebaker 	 * this function is protected by start_op, so a recovery
22945151Swebaker 	 * cannot begin until we are out of here.
22950Sstevel@tonic-gate 	 */
22965151Swebaker 	mutex_enter(&mi->mi_lock);
22975151Swebaker 	recov = mi->mi_recovflags & MI4_RECOV_ACTIV;
22985151Swebaker 	mutex_exit(&mi->mi_lock);
22995151Swebaker 
23005151Swebaker 	mutex_enter(&rp->r_statev4_lock);
23015151Swebaker 
23020Sstevel@tonic-gate 	if (nfs4_delegreturn_policy == IMMEDIATE || !valid_garp)
23035151Swebaker 		dr_needed = TRUE;
23045151Swebaker 
23055151Swebaker 	if (dr_needed && rp->r_deleg_return_pending == FALSE) {
23065151Swebaker 		if (recov) {
23075151Swebaker 			/*
23085151Swebaker 			 * We cannot call delegreturn from inside
23095151Swebaker 			 * of recovery or VOP_PUTPAGE will hang
23105151Swebaker 			 * due to nfs4_start_fop call in
23115151Swebaker 			 * nfs4write.  Use dlistadd to add the
23125151Swebaker 			 * rnode to the list of rnodes needing
23135151Swebaker 			 * cleaning.  We do not need to do reopen
23145151Swebaker 			 * here because recov_openfiles will do it.
23155151Swebaker 			 * In the non-recall case, just discard the
23165151Swebaker 			 * delegation as it is no longer valid.
23175151Swebaker 			 */
23185151Swebaker 			if (recall)
23195151Swebaker 				dr_flags = NFS4_DR_PUSH;
23205151Swebaker 			else
23215151Swebaker 				dr_flags = NFS4_DR_PUSH|NFS4_DR_DISCARD;
23225151Swebaker 
23235151Swebaker 			nfs4_dlistadd(rp, ncg, dr_flags);
23245151Swebaker 			dr_flags = 0;
23255151Swebaker 		} else {
23265151Swebaker 			/*
23275151Swebaker 			 * Push the modified data back to the server,
23285151Swebaker 			 * reopen any delegation open streams, and return
23295151Swebaker 			 * the delegation.  Drop the statev4_lock first!
23305151Swebaker 			 */
23315151Swebaker 			dr_flags =  NFS4_DR_PUSH|NFS4_DR_DID_OP|NFS4_DR_REOPEN;
23325151Swebaker 		}
23335151Swebaker 	}
23345151Swebaker 	mutex_exit(&rp->r_statev4_lock);
23355151Swebaker 	if (dr_flags)
23365151Swebaker 		(void) nfs4delegreturn_impl(rp, dr_flags, ncg);
23370Sstevel@tonic-gate }
23380Sstevel@tonic-gate 
23390Sstevel@tonic-gate /*
23400Sstevel@tonic-gate  * nfs4delegabandon - Abandon the delegation on an rnode4.  This code
23410Sstevel@tonic-gate  * is called when the client receives EXPIRED, BAD_STATEID, OLD_STATEID
23420Sstevel@tonic-gate  * or BADSEQID and the recovery code is unable to recover.  Push any
23430Sstevel@tonic-gate  * dirty data back to the server and return the delegation (if any).
23440Sstevel@tonic-gate  */
23450Sstevel@tonic-gate 
23460Sstevel@tonic-gate void
nfs4delegabandon(rnode4_t * rp)23470Sstevel@tonic-gate nfs4delegabandon(rnode4_t *rp)
23480Sstevel@tonic-gate {
23490Sstevel@tonic-gate 	vnode_t *vp;
23500Sstevel@tonic-gate 	struct cb_recall_pass *pp;
23510Sstevel@tonic-gate 	open_delegation_type4 dt;
23520Sstevel@tonic-gate 
23530Sstevel@tonic-gate 	mutex_enter(&rp->r_statev4_lock);
23540Sstevel@tonic-gate 	dt = rp->r_deleg_type;
23550Sstevel@tonic-gate 	mutex_exit(&rp->r_statev4_lock);
23560Sstevel@tonic-gate 
23570Sstevel@tonic-gate 	if (dt == OPEN_DELEGATE_NONE)
23580Sstevel@tonic-gate 		return;
23590Sstevel@tonic-gate 
23600Sstevel@tonic-gate 	vp = RTOV4(rp);
23610Sstevel@tonic-gate 	VN_HOLD(vp);
23620Sstevel@tonic-gate 
23630Sstevel@tonic-gate 	pp = kmem_alloc(sizeof (struct cb_recall_pass), KM_SLEEP);
23640Sstevel@tonic-gate 	pp->rp = rp;
23650Sstevel@tonic-gate 	/*
23660Sstevel@tonic-gate 	 * Recovery on the file has failed and we want to return
23670Sstevel@tonic-gate 	 * the delegation.  We don't want to reopen files and
23680Sstevel@tonic-gate 	 * nfs4delegreturn_thread() figures out what to do about
23690Sstevel@tonic-gate 	 * the data.  The only thing to do is attempt to return
23700Sstevel@tonic-gate 	 * the delegation.
23710Sstevel@tonic-gate 	 */
23720Sstevel@tonic-gate 	pp->flags = 0;
23730Sstevel@tonic-gate 	pp->truncate = FALSE;
23740Sstevel@tonic-gate 
23750Sstevel@tonic-gate 	/*
23760Sstevel@tonic-gate 	 * Fire up a thread to do the delegreturn; this is
23770Sstevel@tonic-gate 	 * necessary because we could be inside a GETPAGE or
23780Sstevel@tonic-gate 	 * PUTPAGE and we cannot do another one.
23790Sstevel@tonic-gate 	 */
23800Sstevel@tonic-gate 
23810Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, nfs4delegreturn_thread, pp, 0,
23825151Swebaker 	    minclsyspri);
23830Sstevel@tonic-gate }
23840Sstevel@tonic-gate 
23850Sstevel@tonic-gate static int
wait_for_recall1(vnode_t * vp,nfs4_op_hint_t op,nfs4_recov_state_t * rsp,int flg)23860Sstevel@tonic-gate wait_for_recall1(vnode_t *vp, nfs4_op_hint_t op, nfs4_recov_state_t *rsp,
23870Sstevel@tonic-gate 	int flg)
23880Sstevel@tonic-gate {
23890Sstevel@tonic-gate 	rnode4_t *rp;
23900Sstevel@tonic-gate 	int error = 0;
23910Sstevel@tonic-gate 
23920Sstevel@tonic-gate #ifdef lint
23930Sstevel@tonic-gate 	op = op;
23940Sstevel@tonic-gate #endif
23950Sstevel@tonic-gate 
23960Sstevel@tonic-gate 	if (vp && vp->v_type == VREG) {
23970Sstevel@tonic-gate 		rp = VTOR4(vp);
23980Sstevel@tonic-gate 
23990Sstevel@tonic-gate 		/*
24000Sstevel@tonic-gate 		 * Take r_deleg_recall_lock in read mode to synchronize
24010Sstevel@tonic-gate 		 * with delegreturn.
24020Sstevel@tonic-gate 		 */
24030Sstevel@tonic-gate 		error = nfs_rw_enter_sig(&rp->r_deleg_recall_lock,
24045151Swebaker 		    RW_READER, INTR4(vp));
24050Sstevel@tonic-gate 
24060Sstevel@tonic-gate 		if (error == 0)
24070Sstevel@tonic-gate 			rsp->rs_flags |= flg;
24080Sstevel@tonic-gate 
24090Sstevel@tonic-gate 	}
24100Sstevel@tonic-gate 	return (error);
24110Sstevel@tonic-gate }
24120Sstevel@tonic-gate 
24130Sstevel@tonic-gate void
nfs4_end_op_recall(vnode_t * vp1,vnode_t * vp2,nfs4_recov_state_t * rsp)24140Sstevel@tonic-gate nfs4_end_op_recall(vnode_t *vp1, vnode_t *vp2, nfs4_recov_state_t *rsp)
24150Sstevel@tonic-gate {
24160Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_recall_debug,
24175151Swebaker 	    (CE_NOTE, "nfs4_end_op_recall: 0x%p, 0x%p\n",
24185151Swebaker 	    (void *)vp1, (void *)vp2));
24190Sstevel@tonic-gate 
24200Sstevel@tonic-gate 	if (vp2 && rsp->rs_flags & NFS4_RS_RECALL_HELD2)
24210Sstevel@tonic-gate 		nfs_rw_exit(&VTOR4(vp2)->r_deleg_recall_lock);
24220Sstevel@tonic-gate 	if (vp1 && rsp->rs_flags & NFS4_RS_RECALL_HELD1)
24230Sstevel@tonic-gate 		nfs_rw_exit(&VTOR4(vp1)->r_deleg_recall_lock);
24240Sstevel@tonic-gate }
24250Sstevel@tonic-gate 
24260Sstevel@tonic-gate int
wait_for_recall(vnode_t * vp1,vnode_t * vp2,nfs4_op_hint_t op,nfs4_recov_state_t * rsp)24270Sstevel@tonic-gate wait_for_recall(vnode_t *vp1, vnode_t *vp2, nfs4_op_hint_t op,
24280Sstevel@tonic-gate 	nfs4_recov_state_t *rsp)
24290Sstevel@tonic-gate {
24300Sstevel@tonic-gate 	int error;
24310Sstevel@tonic-gate 
24320Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_recall_debug,
24335151Swebaker 	    (CE_NOTE, "wait_for_recall:    0x%p, 0x%p\n",
24345151Swebaker 	    (void *)vp1, (void *) vp2));
24350Sstevel@tonic-gate 
24360Sstevel@tonic-gate 	rsp->rs_flags &= ~(NFS4_RS_RECALL_HELD1|NFS4_RS_RECALL_HELD2);
24370Sstevel@tonic-gate 
24380Sstevel@tonic-gate 	if ((error = wait_for_recall1(vp1, op, rsp, NFS4_RS_RECALL_HELD1)) != 0)
24390Sstevel@tonic-gate 		return (error);
24400Sstevel@tonic-gate 
24410Sstevel@tonic-gate 	if ((error = wait_for_recall1(vp2, op, rsp, NFS4_RS_RECALL_HELD2))
24420Sstevel@tonic-gate 	    != 0) {
24430Sstevel@tonic-gate 		if (rsp->rs_flags & NFS4_RS_RECALL_HELD1) {
24440Sstevel@tonic-gate 			nfs_rw_exit(&VTOR4(vp1)->r_deleg_recall_lock);
24450Sstevel@tonic-gate 			rsp->rs_flags &= ~NFS4_RS_RECALL_HELD1;
24460Sstevel@tonic-gate 		}
24470Sstevel@tonic-gate 
24480Sstevel@tonic-gate 		return (error);
24490Sstevel@tonic-gate 	}
24500Sstevel@tonic-gate 
24510Sstevel@tonic-gate 	return (0);
24520Sstevel@tonic-gate }
24530Sstevel@tonic-gate 
24540Sstevel@tonic-gate /*
24550Sstevel@tonic-gate  * nfs4_dlistadd - Add this rnode to a list of rnodes to be
24560Sstevel@tonic-gate  * DELEGRETURN'd at the end of recovery.
24570Sstevel@tonic-gate  */
24580Sstevel@tonic-gate 
24590Sstevel@tonic-gate static void
nfs4_dlistadd(rnode4_t * rp,struct nfs4_callback_globals * ncg,int flags)24600Sstevel@tonic-gate nfs4_dlistadd(rnode4_t *rp, struct nfs4_callback_globals *ncg, int flags)
24610Sstevel@tonic-gate {
24620Sstevel@tonic-gate 	struct nfs4_dnode *dp;
24630Sstevel@tonic-gate 
24640Sstevel@tonic-gate 	ASSERT(mutex_owned(&rp->r_statev4_lock));
24650Sstevel@tonic-gate 	/*
24660Sstevel@tonic-gate 	 * Mark the delegation as having a return pending.
24670Sstevel@tonic-gate 	 * This will prevent the use of the delegation stateID
24680Sstevel@tonic-gate 	 * by read, write, setattr and open.
24690Sstevel@tonic-gate 	 */
24700Sstevel@tonic-gate 	rp->r_deleg_return_pending = TRUE;
24710Sstevel@tonic-gate 	dp = kmem_alloc(sizeof (*dp), KM_SLEEP);
24720Sstevel@tonic-gate 	VN_HOLD(RTOV4(rp));
24730Sstevel@tonic-gate 	dp->rnodep = rp;
24740Sstevel@tonic-gate 	dp->flags = flags;
24750Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_dlist_lock);
24760Sstevel@tonic-gate 	list_insert_head(&ncg->nfs4_dlist, dp);
24770Sstevel@tonic-gate #ifdef	DEBUG
24780Sstevel@tonic-gate 	ncg->nfs4_dlistadd_c++;
24790Sstevel@tonic-gate #endif
24800Sstevel@tonic-gate 	mutex_exit(&ncg->nfs4_dlist_lock);
24810Sstevel@tonic-gate }
24820Sstevel@tonic-gate 
24830Sstevel@tonic-gate /*
24840Sstevel@tonic-gate  * nfs4_dlistclean_impl - Do DELEGRETURN for each rnode on the list.
24850Sstevel@tonic-gate  * of files awaiting cleaning.  If the override_flags are non-zero
24860Sstevel@tonic-gate  * then use them rather than the flags that were set when the rnode
24870Sstevel@tonic-gate  * was added to the dlist.
24880Sstevel@tonic-gate  */
24890Sstevel@tonic-gate static void
nfs4_dlistclean_impl(struct nfs4_callback_globals * ncg,int override_flags)24900Sstevel@tonic-gate nfs4_dlistclean_impl(struct nfs4_callback_globals *ncg, int override_flags)
24910Sstevel@tonic-gate {
24920Sstevel@tonic-gate 	rnode4_t *rp;
24930Sstevel@tonic-gate 	struct nfs4_dnode *dp;
24940Sstevel@tonic-gate 	int flags;
24950Sstevel@tonic-gate 
24960Sstevel@tonic-gate 	ASSERT(override_flags == 0 || override_flags == NFS4_DR_DISCARD);
24970Sstevel@tonic-gate 
24980Sstevel@tonic-gate 	mutex_enter(&ncg->nfs4_dlist_lock);
24990Sstevel@tonic-gate 	while ((dp = list_head(&ncg->nfs4_dlist)) != NULL) {
25000Sstevel@tonic-gate #ifdef	DEBUG
25010Sstevel@tonic-gate 		ncg->nfs4_dlistclean_c++;
25020Sstevel@tonic-gate #endif
25030Sstevel@tonic-gate 		list_remove(&ncg->nfs4_dlist, dp);
25040Sstevel@tonic-gate 		mutex_exit(&ncg->nfs4_dlist_lock);
25050Sstevel@tonic-gate 		rp = dp->rnodep;
25060Sstevel@tonic-gate 		flags = (override_flags != 0) ? override_flags : dp->flags;
25070Sstevel@tonic-gate 		kmem_free(dp, sizeof (*dp));
25080Sstevel@tonic-gate 		(void) nfs4delegreturn_impl(rp, flags, ncg);
25090Sstevel@tonic-gate 		VN_RELE(RTOV4(rp));
25100Sstevel@tonic-gate 		mutex_enter(&ncg->nfs4_dlist_lock);
25110Sstevel@tonic-gate 	}
25120Sstevel@tonic-gate 	mutex_exit(&ncg->nfs4_dlist_lock);
25130Sstevel@tonic-gate }
25140Sstevel@tonic-gate 
25150Sstevel@tonic-gate void
nfs4_dlistclean(void)25160Sstevel@tonic-gate nfs4_dlistclean(void)
25170Sstevel@tonic-gate {
25180Sstevel@tonic-gate 	struct nfs4_callback_globals *ncg;
25190Sstevel@tonic-gate 
2520766Scarlsonj 	ncg = zone_getspecific(nfs4_callback_zone_key, nfs_zone());
25210Sstevel@tonic-gate 	ASSERT(ncg != NULL);
25220Sstevel@tonic-gate 
25230Sstevel@tonic-gate 	nfs4_dlistclean_impl(ncg, 0);
25240Sstevel@tonic-gate }
2525