1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
30*7c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
31*7c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
32*7c478bd9Sstevel@tonic-gate #include <nfs/nfs4_kprot.h>
33*7c478bd9Sstevel@tonic-gate #include <nfs/nfs4.h>
34*7c478bd9Sstevel@tonic-gate #include <nfs/lm.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/disp.h>
37*7c478bd9Sstevel@tonic-gate 
38*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
39*7c478bd9Sstevel@tonic-gate 
40*7c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate #include <inet/common.h>
46*7c478bd9Sstevel@tonic-gate #include <inet/ip.h>
47*7c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
48*7c478bd9Sstevel@tonic-gate 
49*7c478bd9Sstevel@tonic-gate #define	MAX_READ_DELEGATIONS 5
50*7c478bd9Sstevel@tonic-gate 
51*7c478bd9Sstevel@tonic-gate krwlock_t rfs4_deleg_policy_lock;
52*7c478bd9Sstevel@tonic-gate srv_deleg_policy_t rfs4_deleg_policy = SRV_NEVER_DELEGATE;
53*7c478bd9Sstevel@tonic-gate static int rfs4_deleg_wlp = 5;
54*7c478bd9Sstevel@tonic-gate kmutex_t rfs4_deleg_lock;
55*7c478bd9Sstevel@tonic-gate static int rfs4_deleg_disabled;
56*7c478bd9Sstevel@tonic-gate 
57*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
58*7c478bd9Sstevel@tonic-gate 
59*7c478bd9Sstevel@tonic-gate static int rfs4_test_cbgetattr_fail = 0;
60*7c478bd9Sstevel@tonic-gate int rfs4_cb_null;
61*7c478bd9Sstevel@tonic-gate int rfs4_cb_debug;
62*7c478bd9Sstevel@tonic-gate int rfs4_deleg_debug;
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate #endif
65*7c478bd9Sstevel@tonic-gate 
66*7c478bd9Sstevel@tonic-gate static void rfs4_recall_file(rfs4_file_t *,
67*7c478bd9Sstevel@tonic-gate 			    void (*recall)(rfs4_deleg_state_t *, bool_t),
68*7c478bd9Sstevel@tonic-gate 			    bool_t, rfs4_client_t *);
69*7c478bd9Sstevel@tonic-gate static	void		rfs4_revoke_deleg(rfs4_deleg_state_t *);
70*7c478bd9Sstevel@tonic-gate static	void		rfs4_revoke_file(rfs4_file_t *);
71*7c478bd9Sstevel@tonic-gate static	void		rfs4_cb_chflush(rfs4_cbinfo_t *);
72*7c478bd9Sstevel@tonic-gate static	CLIENT		*rfs4_cb_getch(rfs4_cbinfo_t *);
73*7c478bd9Sstevel@tonic-gate static	void		rfs4_cb_freech(rfs4_cbinfo_t *, CLIENT *, bool_t);
74*7c478bd9Sstevel@tonic-gate static rfs4_deleg_state_t *rfs4_deleg_state(rfs4_state_t *,
75*7c478bd9Sstevel@tonic-gate 				open_delegation_type4, int *);
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate /*
78*7c478bd9Sstevel@tonic-gate  * Convert a universal address to an transport specific
79*7c478bd9Sstevel@tonic-gate  * address using inet_pton.
80*7c478bd9Sstevel@tonic-gate  */
81*7c478bd9Sstevel@tonic-gate static int
82*7c478bd9Sstevel@tonic-gate uaddr2sockaddr(int af, char *ua, void *ap, in_port_t *pp)
83*7c478bd9Sstevel@tonic-gate {
84*7c478bd9Sstevel@tonic-gate 	int dots = 0, i, j, len, k;
85*7c478bd9Sstevel@tonic-gate 	unsigned char c;
86*7c478bd9Sstevel@tonic-gate 	in_port_t port = 0;
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate 	len = strlen(ua);
89*7c478bd9Sstevel@tonic-gate 
90*7c478bd9Sstevel@tonic-gate 	for (i = len-1; i >= 0; i--) {
91*7c478bd9Sstevel@tonic-gate 
92*7c478bd9Sstevel@tonic-gate 		if (ua[i] == '.')
93*7c478bd9Sstevel@tonic-gate 			dots++;
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate 		if (dots == 2) {
96*7c478bd9Sstevel@tonic-gate 
97*7c478bd9Sstevel@tonic-gate 			ua[i] = '\0';
98*7c478bd9Sstevel@tonic-gate 			/*
99*7c478bd9Sstevel@tonic-gate 			 * We use k to remember were to stick '.' back, since
100*7c478bd9Sstevel@tonic-gate 			 * ua was kmem_allocateded from the pool len+1.
101*7c478bd9Sstevel@tonic-gate 			 */
102*7c478bd9Sstevel@tonic-gate 			k = i;
103*7c478bd9Sstevel@tonic-gate 			if (inet_pton(af, ua, ap) == 1) {
104*7c478bd9Sstevel@tonic-gate 
105*7c478bd9Sstevel@tonic-gate 				c = 0;
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate 				for (j = i+1; j < len; j++) {
108*7c478bd9Sstevel@tonic-gate 					if (ua[j] == '.') {
109*7c478bd9Sstevel@tonic-gate 						port = c << 8;
110*7c478bd9Sstevel@tonic-gate 						c = 0;
111*7c478bd9Sstevel@tonic-gate 					} else if (ua[j] >= '0' &&
112*7c478bd9Sstevel@tonic-gate 					    ua[j] <= '9') {
113*7c478bd9Sstevel@tonic-gate 						c *= 10;
114*7c478bd9Sstevel@tonic-gate 						c += ua[j] - '0';
115*7c478bd9Sstevel@tonic-gate 					} else {
116*7c478bd9Sstevel@tonic-gate 						ua[k] = '.';
117*7c478bd9Sstevel@tonic-gate 						return (EINVAL);
118*7c478bd9Sstevel@tonic-gate 					}
119*7c478bd9Sstevel@tonic-gate 				}
120*7c478bd9Sstevel@tonic-gate 				port += c;
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate 				/* reset to network order */
124*7c478bd9Sstevel@tonic-gate 				if (af == AF_INET) {
125*7c478bd9Sstevel@tonic-gate 					*(uint32_t *)ap =
126*7c478bd9Sstevel@tonic-gate 						htonl(*(uint32_t *)ap);
127*7c478bd9Sstevel@tonic-gate 					*pp = htons(port);
128*7c478bd9Sstevel@tonic-gate 				} else {
129*7c478bd9Sstevel@tonic-gate 					int ix;
130*7c478bd9Sstevel@tonic-gate 					uint16_t *sap;
131*7c478bd9Sstevel@tonic-gate 
132*7c478bd9Sstevel@tonic-gate 					for (sap = ap, ix = 0; ix <
133*7c478bd9Sstevel@tonic-gate 					    sizeof (struct in6_addr) /
134*7c478bd9Sstevel@tonic-gate 					    sizeof (uint16_t); ix++)
135*7c478bd9Sstevel@tonic-gate 						sap[ix] = htons(sap[ix]);
136*7c478bd9Sstevel@tonic-gate 
137*7c478bd9Sstevel@tonic-gate 					*pp = htons(port);
138*7c478bd9Sstevel@tonic-gate 				}
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate 				ua[k] = '.';
141*7c478bd9Sstevel@tonic-gate 				return (0);
142*7c478bd9Sstevel@tonic-gate 			} else {
143*7c478bd9Sstevel@tonic-gate 				ua[k] = '.';
144*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
145*7c478bd9Sstevel@tonic-gate 			}
146*7c478bd9Sstevel@tonic-gate 		}
147*7c478bd9Sstevel@tonic-gate 	}
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate 	return (EINVAL);
150*7c478bd9Sstevel@tonic-gate }
151*7c478bd9Sstevel@tonic-gate 
152*7c478bd9Sstevel@tonic-gate /*
153*7c478bd9Sstevel@tonic-gate  * Update the delegation policy with the
154*7c478bd9Sstevel@tonic-gate  * value of "new_policy"
155*7c478bd9Sstevel@tonic-gate  */
156*7c478bd9Sstevel@tonic-gate void
157*7c478bd9Sstevel@tonic-gate rfs4_set_deleg_policy(srv_deleg_policy_t new_policy)
158*7c478bd9Sstevel@tonic-gate {
159*7c478bd9Sstevel@tonic-gate 	rw_enter(&rfs4_deleg_policy_lock, RW_WRITER);
160*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_policy = new_policy;
161*7c478bd9Sstevel@tonic-gate 	rw_exit(&rfs4_deleg_policy_lock);
162*7c478bd9Sstevel@tonic-gate }
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate void
165*7c478bd9Sstevel@tonic-gate rfs4_hold_deleg_policy(void)
166*7c478bd9Sstevel@tonic-gate {
167*7c478bd9Sstevel@tonic-gate 	rw_enter(&rfs4_deleg_policy_lock, RW_READER);
168*7c478bd9Sstevel@tonic-gate }
169*7c478bd9Sstevel@tonic-gate 
170*7c478bd9Sstevel@tonic-gate void
171*7c478bd9Sstevel@tonic-gate rfs4_rele_deleg_policy(void)
172*7c478bd9Sstevel@tonic-gate {
173*7c478bd9Sstevel@tonic-gate 	rw_exit(&rfs4_deleg_policy_lock);
174*7c478bd9Sstevel@tonic-gate }
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate /*
178*7c478bd9Sstevel@tonic-gate  * This free function is to be used when the client struct is being
179*7c478bd9Sstevel@tonic-gate  * released and nothing at all is needed of the callback info any
180*7c478bd9Sstevel@tonic-gate  * longer.
181*7c478bd9Sstevel@tonic-gate  */
182*7c478bd9Sstevel@tonic-gate void
183*7c478bd9Sstevel@tonic-gate rfs4_cbinfo_free(rfs4_cbinfo_t *cbp)
184*7c478bd9Sstevel@tonic-gate {
185*7c478bd9Sstevel@tonic-gate 	char *addr = cbp->cb_callback.cb_location.r_addr;
186*7c478bd9Sstevel@tonic-gate 	char *netid = cbp->cb_callback.cb_location.r_netid;
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate 	/* Free old address if any */
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate 	if (addr)
191*7c478bd9Sstevel@tonic-gate 		kmem_free(addr, strlen(addr) + 1);
192*7c478bd9Sstevel@tonic-gate 	if (netid)
193*7c478bd9Sstevel@tonic-gate 		kmem_free(netid, strlen(netid) + 1);
194*7c478bd9Sstevel@tonic-gate 
195*7c478bd9Sstevel@tonic-gate 	addr = cbp->cb_newer.cb_callback.cb_location.r_addr;
196*7c478bd9Sstevel@tonic-gate 	netid = cbp->cb_newer.cb_callback.cb_location.r_netid;
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate 	if (addr)
199*7c478bd9Sstevel@tonic-gate 		kmem_free(addr, strlen(addr) + 1);
200*7c478bd9Sstevel@tonic-gate 	if (netid)
201*7c478bd9Sstevel@tonic-gate 		kmem_free(netid, strlen(netid) + 1);
202*7c478bd9Sstevel@tonic-gate 
203*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_chc_free) {
204*7c478bd9Sstevel@tonic-gate 		rfs4_cb_chflush(cbp);
205*7c478bd9Sstevel@tonic-gate 	}
206*7c478bd9Sstevel@tonic-gate }
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate /*
209*7c478bd9Sstevel@tonic-gate  * The server uses this to check the callback path supplied by the
210*7c478bd9Sstevel@tonic-gate  * client.  The callback connection is marked "in progress" while this
211*7c478bd9Sstevel@tonic-gate  * work is going on and then eventually marked either OK or FAILED.
212*7c478bd9Sstevel@tonic-gate  * This work can be done as part of a separate thread and at the end
213*7c478bd9Sstevel@tonic-gate  * of this the thread will exit or it may be done such that the caller
214*7c478bd9Sstevel@tonic-gate  * will continue with other work.
215*7c478bd9Sstevel@tonic-gate  */
216*7c478bd9Sstevel@tonic-gate static void
217*7c478bd9Sstevel@tonic-gate rfs4_do_cb_null(rfs4_client_t *cp)
218*7c478bd9Sstevel@tonic-gate {
219*7c478bd9Sstevel@tonic-gate 	struct timeval tv;
220*7c478bd9Sstevel@tonic-gate 	CLIENT *ch;
221*7c478bd9Sstevel@tonic-gate 	rfs4_cbstate_t newstate;
222*7c478bd9Sstevel@tonic-gate 	rfs4_cbinfo_t *cbp = &cp->cbinfo;
223*7c478bd9Sstevel@tonic-gate 
224*7c478bd9Sstevel@tonic-gate 	if (cp == NULL) {
225*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
226*7c478bd9Sstevel@tonic-gate 			"rfs4_do_cb_null: no rfs4_client specified\n"));
227*7c478bd9Sstevel@tonic-gate 		return;
228*7c478bd9Sstevel@tonic-gate 	}
229*7c478bd9Sstevel@tonic-gate 
230*7c478bd9Sstevel@tonic-gate 	mutex_enter(cbp->cb_lock);
231*7c478bd9Sstevel@tonic-gate 	/* If another thread is doing CB_NULL RPC then return */
232*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_nullcaller == TRUE) {
233*7c478bd9Sstevel@tonic-gate 		mutex_exit(cbp->cb_lock);
234*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp);
235*7c478bd9Sstevel@tonic-gate 		return;
236*7c478bd9Sstevel@tonic-gate 	}
237*7c478bd9Sstevel@tonic-gate 
238*7c478bd9Sstevel@tonic-gate 	/* Mark the cbinfo as having a thread in the NULL callback */
239*7c478bd9Sstevel@tonic-gate 	cbp->cb_nullcaller = TRUE;
240*7c478bd9Sstevel@tonic-gate 
241*7c478bd9Sstevel@tonic-gate 	/*
242*7c478bd9Sstevel@tonic-gate 	 * Are there other threads still using the cbinfo client
243*7c478bd9Sstevel@tonic-gate 	 * handles?  If so, this thread must wait before going and
244*7c478bd9Sstevel@tonic-gate 	 * mucking aroiund with the callback information
245*7c478bd9Sstevel@tonic-gate 	 */
246*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_refcnt != 0)
247*7c478bd9Sstevel@tonic-gate 		cv_wait(cbp->cb_cv_nullcaller, cbp->cb_lock);
248*7c478bd9Sstevel@tonic-gate 
249*7c478bd9Sstevel@tonic-gate 	/*
250*7c478bd9Sstevel@tonic-gate 	 * This thread itself may find that new callback info has
251*7c478bd9Sstevel@tonic-gate 	 * arrived and is set up to handle this case and redrive the
252*7c478bd9Sstevel@tonic-gate 	 * call to the client's callback server.
253*7c478bd9Sstevel@tonic-gate 	 */
254*7c478bd9Sstevel@tonic-gate retry:
255*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_newer.cb_new == TRUE &&
256*7c478bd9Sstevel@tonic-gate 	    cbp->cb_newer.cb_confirmed == TRUE) {
257*7c478bd9Sstevel@tonic-gate 		char *addr = cbp->cb_callback.cb_location.r_addr;
258*7c478bd9Sstevel@tonic-gate 		char *netid = cbp->cb_callback.cb_location.r_netid;
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate 		/*
261*7c478bd9Sstevel@tonic-gate 		 * Free the old stuff if it exists; may be the first
262*7c478bd9Sstevel@tonic-gate 		 * time through this path
263*7c478bd9Sstevel@tonic-gate 		 */
264*7c478bd9Sstevel@tonic-gate 		if (addr)
265*7c478bd9Sstevel@tonic-gate 			kmem_free(addr, strlen(addr) + 1);
266*7c478bd9Sstevel@tonic-gate 		if (netid)
267*7c478bd9Sstevel@tonic-gate 			kmem_free(netid, strlen(netid) + 1);
268*7c478bd9Sstevel@tonic-gate 
269*7c478bd9Sstevel@tonic-gate 		/* Move over the addr/netid */
270*7c478bd9Sstevel@tonic-gate 		cbp->cb_callback.cb_location.r_addr =
271*7c478bd9Sstevel@tonic-gate 			cbp->cb_newer.cb_callback.cb_location.r_addr;
272*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_callback.cb_location.r_addr = NULL;
273*7c478bd9Sstevel@tonic-gate 		cbp->cb_callback.cb_location.r_netid =
274*7c478bd9Sstevel@tonic-gate 			cbp->cb_newer.cb_callback.cb_location.r_netid;
275*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_callback.cb_location.r_netid = NULL;
276*7c478bd9Sstevel@tonic-gate 
277*7c478bd9Sstevel@tonic-gate 		/* Get the program number */
278*7c478bd9Sstevel@tonic-gate 		cbp->cb_callback.cb_program =
279*7c478bd9Sstevel@tonic-gate 			cbp->cb_newer.cb_callback.cb_program;
280*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_callback.cb_program = 0;
281*7c478bd9Sstevel@tonic-gate 
282*7c478bd9Sstevel@tonic-gate 		/* Don't forget the protocol's "cb_ident" field */
283*7c478bd9Sstevel@tonic-gate 		cbp->cb_ident = cbp->cb_newer.cb_ident;
284*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_ident = 0;
285*7c478bd9Sstevel@tonic-gate 
286*7c478bd9Sstevel@tonic-gate 		/* no longer new */
287*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_new = FALSE;
288*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_confirmed = FALSE;
289*7c478bd9Sstevel@tonic-gate 
290*7c478bd9Sstevel@tonic-gate 		/* get rid of the old client handles that may exist */
291*7c478bd9Sstevel@tonic-gate 		rfs4_cb_chflush(cbp);
292*7c478bd9Sstevel@tonic-gate 
293*7c478bd9Sstevel@tonic-gate 		cbp->cb_state = CB_NONE;
294*7c478bd9Sstevel@tonic-gate 		cbp->cb_timefailed = 0; /* reset the clock */
295*7c478bd9Sstevel@tonic-gate 		cbp->cb_notified_of_cb_path_down = TRUE;
296*7c478bd9Sstevel@tonic-gate 	}
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_state != CB_NONE) {
299*7c478bd9Sstevel@tonic-gate 		cv_broadcast(cbp->cb_cv);	/* let the others know */
300*7c478bd9Sstevel@tonic-gate 		cbp->cb_nullcaller = FALSE;
301*7c478bd9Sstevel@tonic-gate 		mutex_exit(cbp->cb_lock);
302*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp);
303*7c478bd9Sstevel@tonic-gate 		return;
304*7c478bd9Sstevel@tonic-gate 	}
305*7c478bd9Sstevel@tonic-gate 
306*7c478bd9Sstevel@tonic-gate 	/* mark rfs4_client_t as CALLBACK NULL in progress */
307*7c478bd9Sstevel@tonic-gate 	cbp->cb_state = CB_INPROG;
308*7c478bd9Sstevel@tonic-gate 	mutex_exit(cbp->cb_lock);
309*7c478bd9Sstevel@tonic-gate 
310*7c478bd9Sstevel@tonic-gate 	/* get/generate a client handle */
311*7c478bd9Sstevel@tonic-gate 	if ((ch = rfs4_cb_getch(cbp)) == NULL) {
312*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
313*7c478bd9Sstevel@tonic-gate 			"rfs4_do_cb_null: failed to get client handle\n"));
314*7c478bd9Sstevel@tonic-gate 		mutex_enter(cbp->cb_lock);
315*7c478bd9Sstevel@tonic-gate 		cbp->cb_state = CB_BAD;
316*7c478bd9Sstevel@tonic-gate 		cbp->cb_timefailed = gethrestime_sec(); /* observability */
317*7c478bd9Sstevel@tonic-gate 		goto retry;
318*7c478bd9Sstevel@tonic-gate 	}
319*7c478bd9Sstevel@tonic-gate 
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	tv.tv_sec = 30;
322*7c478bd9Sstevel@tonic-gate 	tv.tv_usec = 0;
323*7c478bd9Sstevel@tonic-gate 	if (clnt_call(ch, CB_NULL, xdr_void, NULL, xdr_void, NULL, tv) != 0) {
324*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
325*7c478bd9Sstevel@tonic-gate 			"rfs4_do_cb_null: clnt_call failed\n"));
326*7c478bd9Sstevel@tonic-gate 
327*7c478bd9Sstevel@tonic-gate 		newstate = CB_BAD;
328*7c478bd9Sstevel@tonic-gate 	} else {
329*7c478bd9Sstevel@tonic-gate 		newstate = CB_OK;
330*7c478bd9Sstevel@tonic-gate #ifdef	DEBUG
331*7c478bd9Sstevel@tonic-gate 		rfs4_cb_null++;
332*7c478bd9Sstevel@tonic-gate #endif
333*7c478bd9Sstevel@tonic-gate 	}
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate 	/* Check to see if the client has specified new callback info */
336*7c478bd9Sstevel@tonic-gate 	mutex_enter(cbp->cb_lock);
337*7c478bd9Sstevel@tonic-gate 	rfs4_cb_freech(cbp, ch, TRUE);
338*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_newer.cb_new == TRUE &&
339*7c478bd9Sstevel@tonic-gate 	    cbp->cb_newer.cb_confirmed == TRUE) {
340*7c478bd9Sstevel@tonic-gate 		goto retry;	/* give the CB_NULL another chance */
341*7c478bd9Sstevel@tonic-gate 	}
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate 	cbp->cb_state = newstate;
344*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_state == CB_BAD)
345*7c478bd9Sstevel@tonic-gate 		cbp->cb_timefailed = gethrestime_sec(); /* observability */
346*7c478bd9Sstevel@tonic-gate 
347*7c478bd9Sstevel@tonic-gate 	cv_broadcast(cbp->cb_cv);	/* start up the other threads */
348*7c478bd9Sstevel@tonic-gate 	cbp->cb_nullcaller = FALSE;
349*7c478bd9Sstevel@tonic-gate 	mutex_exit(cbp->cb_lock);
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 	rfs4_client_rele(cp);
352*7c478bd9Sstevel@tonic-gate }
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate /*
355*7c478bd9Sstevel@tonic-gate  * Given a client struct, inspect the callback info to see if the
356*7c478bd9Sstevel@tonic-gate  * callback path is up and available.  If it is being initialized,
357*7c478bd9Sstevel@tonic-gate  * then wait for the CB_NULL RPC call to occur.
358*7c478bd9Sstevel@tonic-gate  */
359*7c478bd9Sstevel@tonic-gate static rfs4_cbinfo_t *
360*7c478bd9Sstevel@tonic-gate rfs4_cbinfo_hold(rfs4_client_t *cp)
361*7c478bd9Sstevel@tonic-gate {
362*7c478bd9Sstevel@tonic-gate 	rfs4_cbinfo_t *cbp = &cp->cbinfo;
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate retry:
365*7c478bd9Sstevel@tonic-gate 	mutex_enter(cbp->cb_lock);
366*7c478bd9Sstevel@tonic-gate 
367*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_newer.cb_new == TRUE && cbp->cb_nullcaller == FALSE) {
368*7c478bd9Sstevel@tonic-gate 		/*
369*7c478bd9Sstevel@tonic-gate 		 * Looks like a new callback path may be available and
370*7c478bd9Sstevel@tonic-gate 		 * noone has set it up.
371*7c478bd9Sstevel@tonic-gate 		 */
372*7c478bd9Sstevel@tonic-gate 		mutex_exit(cbp->cb_lock);
373*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_hold(cp->dbe);
374*7c478bd9Sstevel@tonic-gate 		rfs4_do_cb_null(cp); /* caller will release client hold */
375*7c478bd9Sstevel@tonic-gate 		goto retry;
376*7c478bd9Sstevel@tonic-gate 	}
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate 	/* Is there a thread working on doing the CB_NULL RPC? */
379*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_nullcaller == TRUE)
380*7c478bd9Sstevel@tonic-gate 		cv_wait(cbp->cb_cv, cbp->cb_lock);  /* if so, wait on it */
381*7c478bd9Sstevel@tonic-gate 
382*7c478bd9Sstevel@tonic-gate 	/* If the callback path is not okay (up and running), just quit */
383*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_state != CB_OK) {
384*7c478bd9Sstevel@tonic-gate 		mutex_exit(cbp->cb_lock);
385*7c478bd9Sstevel@tonic-gate 		return (NULL);
386*7c478bd9Sstevel@tonic-gate 	}
387*7c478bd9Sstevel@tonic-gate 
388*7c478bd9Sstevel@tonic-gate 	/* Let someone know we are using the current callback info */
389*7c478bd9Sstevel@tonic-gate 	cbp->cb_refcnt++;
390*7c478bd9Sstevel@tonic-gate 	mutex_exit(cbp->cb_lock);
391*7c478bd9Sstevel@tonic-gate 	return (cbp);
392*7c478bd9Sstevel@tonic-gate }
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate /*
395*7c478bd9Sstevel@tonic-gate  * The caller is done with the callback info.  It may be that the
396*7c478bd9Sstevel@tonic-gate  * caller's RPC failed and the NFSv4 client has actually provided new
397*7c478bd9Sstevel@tonic-gate  * callback information.  If so, let the caller know so they can
398*7c478bd9Sstevel@tonic-gate  * advantage of this and maybe retry the RPC that originally failed.
399*7c478bd9Sstevel@tonic-gate  */
400*7c478bd9Sstevel@tonic-gate static int
401*7c478bd9Sstevel@tonic-gate rfs4_cbinfo_rele(rfs4_cbinfo_t *cbp, rfs4_cbstate_t newstate)
402*7c478bd9Sstevel@tonic-gate {
403*7c478bd9Sstevel@tonic-gate 	int cb_new = FALSE;
404*7c478bd9Sstevel@tonic-gate 
405*7c478bd9Sstevel@tonic-gate 	mutex_enter(cbp->cb_lock);
406*7c478bd9Sstevel@tonic-gate 
407*7c478bd9Sstevel@tonic-gate 	/* The caller gets a chance to mark the callback info as bad */
408*7c478bd9Sstevel@tonic-gate 	if (newstate != CB_NOCHANGE)
409*7c478bd9Sstevel@tonic-gate 		cbp->cb_state = newstate;
410*7c478bd9Sstevel@tonic-gate 	if (newstate == CB_FAILED) {
411*7c478bd9Sstevel@tonic-gate 		cbp->cb_timefailed = gethrestime_sec(); /* observability */
412*7c478bd9Sstevel@tonic-gate 		cbp->cb_notified_of_cb_path_down = FALSE;
413*7c478bd9Sstevel@tonic-gate 	}
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate 	cbp->cb_refcnt--;	/* no longer using the information */
416*7c478bd9Sstevel@tonic-gate 
417*7c478bd9Sstevel@tonic-gate 	/*
418*7c478bd9Sstevel@tonic-gate 	 * A thread may be waiting on this one to finish and if so,
419*7c478bd9Sstevel@tonic-gate 	 * let it know that it is okay to do the CB_NULL to the
420*7c478bd9Sstevel@tonic-gate 	 * client's callback server.
421*7c478bd9Sstevel@tonic-gate 	 */
422*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_refcnt == 0 && cbp->cb_nullcaller)
423*7c478bd9Sstevel@tonic-gate 		cv_broadcast(cbp->cb_cv_nullcaller);
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate 	/*
426*7c478bd9Sstevel@tonic-gate 	 * If this is the last thread to use the callback info and
427*7c478bd9Sstevel@tonic-gate 	 * there is new callback information to try and no thread is
428*7c478bd9Sstevel@tonic-gate 	 * there ready to do the CB_NULL, then return true to teh
429*7c478bd9Sstevel@tonic-gate 	 * caller so they can do the CB_NULL
430*7c478bd9Sstevel@tonic-gate 	 */
431*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_refcnt == 0 &&
432*7c478bd9Sstevel@tonic-gate 		cbp->cb_nullcaller == FALSE &&
433*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_new == TRUE &&
434*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_confirmed == TRUE)
435*7c478bd9Sstevel@tonic-gate 		cb_new = TRUE;
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate 	mutex_exit(cbp->cb_lock);
438*7c478bd9Sstevel@tonic-gate 
439*7c478bd9Sstevel@tonic-gate 	return (cb_new);
440*7c478bd9Sstevel@tonic-gate }
441*7c478bd9Sstevel@tonic-gate 
442*7c478bd9Sstevel@tonic-gate /*
443*7c478bd9Sstevel@tonic-gate  * Given the information in the callback info struct, create a client
444*7c478bd9Sstevel@tonic-gate  * handle that can be used by the server for its callback path.
445*7c478bd9Sstevel@tonic-gate  */
446*7c478bd9Sstevel@tonic-gate static CLIENT *
447*7c478bd9Sstevel@tonic-gate rfs4_cbch_init(rfs4_cbinfo_t *cbp)
448*7c478bd9Sstevel@tonic-gate {
449*7c478bd9Sstevel@tonic-gate 	struct knetconfig knc;
450*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
451*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in addr4;
452*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 addr6;
453*7c478bd9Sstevel@tonic-gate 	void *addr, *taddr;
454*7c478bd9Sstevel@tonic-gate 	in_port_t *pp;
455*7c478bd9Sstevel@tonic-gate 	int af;
456*7c478bd9Sstevel@tonic-gate 	char *devnam;
457*7c478bd9Sstevel@tonic-gate 	int err = 0;
458*7c478bd9Sstevel@tonic-gate 	struct netbuf nb;
459*7c478bd9Sstevel@tonic-gate 	int size;
460*7c478bd9Sstevel@tonic-gate 	CLIENT *ch = NULL;
461*7c478bd9Sstevel@tonic-gate 	int useresvport = 0;
462*7c478bd9Sstevel@tonic-gate 
463*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
464*7c478bd9Sstevel@tonic-gate 		"rfs4_cbch_init: entry cbp->%p\n", (void *)cbp));
465*7c478bd9Sstevel@tonic-gate 
466*7c478bd9Sstevel@tonic-gate 	mutex_enter(cbp->cb_lock);
467*7c478bd9Sstevel@tonic-gate 
468*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_callback.cb_location.r_netid == NULL ||
469*7c478bd9Sstevel@tonic-gate 	    cbp->cb_callback.cb_location.r_addr == NULL) {
470*7c478bd9Sstevel@tonic-gate 		goto cb_init_out;
471*7c478bd9Sstevel@tonic-gate 	}
472*7c478bd9Sstevel@tonic-gate 
473*7c478bd9Sstevel@tonic-gate 	if (strcmp(cbp->cb_callback.cb_location.r_netid, "tcp") == 0) {
474*7c478bd9Sstevel@tonic-gate 		knc.knc_semantics = NC_TPI_COTS;
475*7c478bd9Sstevel@tonic-gate 		knc.knc_protofmly = "inet";
476*7c478bd9Sstevel@tonic-gate 		knc.knc_proto = "tcp";
477*7c478bd9Sstevel@tonic-gate 		devnam = "/dev/tcp";
478*7c478bd9Sstevel@tonic-gate 		af = AF_INET;
479*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(cbp->cb_callback.cb_location.r_netid, "udp")
480*7c478bd9Sstevel@tonic-gate 		== 0) {
481*7c478bd9Sstevel@tonic-gate 		knc.knc_semantics = NC_TPI_CLTS;
482*7c478bd9Sstevel@tonic-gate 		knc.knc_protofmly = "inet";
483*7c478bd9Sstevel@tonic-gate 		knc.knc_proto = "udp";
484*7c478bd9Sstevel@tonic-gate 		devnam = "/dev/udp";
485*7c478bd9Sstevel@tonic-gate 		af = AF_INET;
486*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(cbp->cb_callback.cb_location.r_netid, "tcp6")
487*7c478bd9Sstevel@tonic-gate 		== 0) {
488*7c478bd9Sstevel@tonic-gate 		knc.knc_semantics = NC_TPI_COTS;
489*7c478bd9Sstevel@tonic-gate 		knc.knc_protofmly = "inet6";
490*7c478bd9Sstevel@tonic-gate 		knc.knc_proto = "tcp";
491*7c478bd9Sstevel@tonic-gate 		devnam = "/dev/tcp6";
492*7c478bd9Sstevel@tonic-gate 		af = AF_INET6;
493*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(cbp->cb_callback.cb_location.r_netid, "udp6")
494*7c478bd9Sstevel@tonic-gate 		== 0) {
495*7c478bd9Sstevel@tonic-gate 		knc.knc_semantics = NC_TPI_CLTS;
496*7c478bd9Sstevel@tonic-gate 		knc.knc_protofmly = "inet6";
497*7c478bd9Sstevel@tonic-gate 		knc.knc_proto = "udp";
498*7c478bd9Sstevel@tonic-gate 		devnam = "/dev/udp6";
499*7c478bd9Sstevel@tonic-gate 		af = AF_INET6;
500*7c478bd9Sstevel@tonic-gate 	} else {
501*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
502*7c478bd9Sstevel@tonic-gate 			"rfs4_cbch_init: unknown transport %s\n",
503*7c478bd9Sstevel@tonic-gate 			cbp->cb_callback.cb_location.r_netid));
504*7c478bd9Sstevel@tonic-gate 
505*7c478bd9Sstevel@tonic-gate 		goto cb_init_out;
506*7c478bd9Sstevel@tonic-gate 	}
507*7c478bd9Sstevel@tonic-gate 
508*7c478bd9Sstevel@tonic-gate 	if ((err = lookupname(devnam, UIO_SYSSPACE, FOLLOW,
509*7c478bd9Sstevel@tonic-gate 	    NULLVPP, &vp)) != 0) {
510*7c478bd9Sstevel@tonic-gate 
511*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
512*7c478bd9Sstevel@tonic-gate 			"rfs4_cbch_init: lookupname failed %d\n", err));
513*7c478bd9Sstevel@tonic-gate 
514*7c478bd9Sstevel@tonic-gate 		goto cb_init_out;
515*7c478bd9Sstevel@tonic-gate 	}
516*7c478bd9Sstevel@tonic-gate 
517*7c478bd9Sstevel@tonic-gate 	if (vp->v_type != VCHR) {
518*7c478bd9Sstevel@tonic-gate 
519*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
520*7c478bd9Sstevel@tonic-gate 			"rfs4_cbch_init: %s is not VCHR", devnam));
521*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
522*7c478bd9Sstevel@tonic-gate 		goto cb_init_out;
523*7c478bd9Sstevel@tonic-gate 	}
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 	knc.knc_rdev = vp->v_rdev;
526*7c478bd9Sstevel@tonic-gate 
527*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
528*7c478bd9Sstevel@tonic-gate 
529*7c478bd9Sstevel@tonic-gate 	if (af == AF_INET) {
530*7c478bd9Sstevel@tonic-gate 		size = sizeof (addr4);
531*7c478bd9Sstevel@tonic-gate 		bzero(&addr4, size);
532*7c478bd9Sstevel@tonic-gate 		addr4.sin_family = (sa_family_t)af;
533*7c478bd9Sstevel@tonic-gate 		addr = &addr4.sin_addr;
534*7c478bd9Sstevel@tonic-gate 		pp = &addr4.sin_port;
535*7c478bd9Sstevel@tonic-gate 		taddr = &addr4;
536*7c478bd9Sstevel@tonic-gate 	} else /* AF_INET6 */ {
537*7c478bd9Sstevel@tonic-gate 		size = sizeof (addr6);
538*7c478bd9Sstevel@tonic-gate 		bzero(&addr6, size);
539*7c478bd9Sstevel@tonic-gate 		addr6.sin6_family = (sa_family_t)af;
540*7c478bd9Sstevel@tonic-gate 		addr = &addr6.sin6_addr;
541*7c478bd9Sstevel@tonic-gate 		pp = &addr6.sin6_port;
542*7c478bd9Sstevel@tonic-gate 		taddr = &addr6;
543*7c478bd9Sstevel@tonic-gate 	}
544*7c478bd9Sstevel@tonic-gate 
545*7c478bd9Sstevel@tonic-gate 	if (uaddr2sockaddr(af,
546*7c478bd9Sstevel@tonic-gate 		cbp->cb_callback.cb_location.r_addr, addr, pp)) {
547*7c478bd9Sstevel@tonic-gate 
548*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
549*7c478bd9Sstevel@tonic-gate 			"rfs4_cbch_init: malformed universal addr: %s\n",
550*7c478bd9Sstevel@tonic-gate 			cbp->cb_callback.cb_location.r_addr));
551*7c478bd9Sstevel@tonic-gate 
552*7c478bd9Sstevel@tonic-gate 		goto cb_init_out;
553*7c478bd9Sstevel@tonic-gate 	}
554*7c478bd9Sstevel@tonic-gate 
555*7c478bd9Sstevel@tonic-gate 
556*7c478bd9Sstevel@tonic-gate 	nb.maxlen = nb.len = size;
557*7c478bd9Sstevel@tonic-gate 	nb.buf = (char *)taddr;
558*7c478bd9Sstevel@tonic-gate 
559*7c478bd9Sstevel@tonic-gate 	if (err = clnt_tli_kcreate(&knc, &nb, cbp->cb_callback.cb_program,
560*7c478bd9Sstevel@tonic-gate 	    NFS_CB, 0, 0, curthread->t_cred, &ch)) {
561*7c478bd9Sstevel@tonic-gate 
562*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
563*7c478bd9Sstevel@tonic-gate 			"rfs4_cbch_init: clnt_tli_kcreate failed %d\n", err));
564*7c478bd9Sstevel@tonic-gate 		ch = NULL;
565*7c478bd9Sstevel@tonic-gate 	}
566*7c478bd9Sstevel@tonic-gate 
567*7c478bd9Sstevel@tonic-gate 	/* turn off reserved port usage */
568*7c478bd9Sstevel@tonic-gate 	(void) CLNT_CONTROL(ch, CLSET_BINDRESVPORT, (char *)&useresvport);
569*7c478bd9Sstevel@tonic-gate 
570*7c478bd9Sstevel@tonic-gate cb_init_out:
571*7c478bd9Sstevel@tonic-gate 	mutex_exit(cbp->cb_lock);
572*7c478bd9Sstevel@tonic-gate 	return (ch);
573*7c478bd9Sstevel@tonic-gate }
574*7c478bd9Sstevel@tonic-gate 
575*7c478bd9Sstevel@tonic-gate /*
576*7c478bd9Sstevel@tonic-gate  * Iterate over the client handle cache and
577*7c478bd9Sstevel@tonic-gate  * destroy it.
578*7c478bd9Sstevel@tonic-gate  */
579*7c478bd9Sstevel@tonic-gate static void
580*7c478bd9Sstevel@tonic-gate rfs4_cb_chflush(rfs4_cbinfo_t *cbp)
581*7c478bd9Sstevel@tonic-gate {
582*7c478bd9Sstevel@tonic-gate 	CLIENT *ch;
583*7c478bd9Sstevel@tonic-gate 
584*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
585*7c478bd9Sstevel@tonic-gate 		"rfs4_cb_flush: enter cbp->%p, cb_chc_free=%d\n",
586*7c478bd9Sstevel@tonic-gate 		(void *)cbp, cbp->cb_chc_free));
587*7c478bd9Sstevel@tonic-gate 
588*7c478bd9Sstevel@tonic-gate 	while (cbp->cb_chc_free) {
589*7c478bd9Sstevel@tonic-gate 		cbp->cb_chc_free--;
590*7c478bd9Sstevel@tonic-gate 		ch = cbp->cb_chc[cbp->cb_chc_free];
591*7c478bd9Sstevel@tonic-gate 		cbp->cb_chc[cbp->cb_chc_free] = NULL;
592*7c478bd9Sstevel@tonic-gate 		if (ch) {
593*7c478bd9Sstevel@tonic-gate 			if (ch->cl_auth)
594*7c478bd9Sstevel@tonic-gate 				auth_destroy(ch->cl_auth);
595*7c478bd9Sstevel@tonic-gate 			clnt_destroy(ch);
596*7c478bd9Sstevel@tonic-gate 		}
597*7c478bd9Sstevel@tonic-gate 	}
598*7c478bd9Sstevel@tonic-gate }
599*7c478bd9Sstevel@tonic-gate 
600*7c478bd9Sstevel@tonic-gate /*
601*7c478bd9Sstevel@tonic-gate  * Return a client handle, either from a the small
602*7c478bd9Sstevel@tonic-gate  * rfs4_client_t cache or one that we just created.
603*7c478bd9Sstevel@tonic-gate  */
604*7c478bd9Sstevel@tonic-gate static CLIENT *
605*7c478bd9Sstevel@tonic-gate rfs4_cb_getch(rfs4_cbinfo_t *cbp)
606*7c478bd9Sstevel@tonic-gate {
607*7c478bd9Sstevel@tonic-gate 	CLIENT *cbch = NULL;
608*7c478bd9Sstevel@tonic-gate 	uint32_t zilch = 0;
609*7c478bd9Sstevel@tonic-gate 
610*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
611*7c478bd9Sstevel@tonic-gate 		"rfs4_cb_getch: enter cbp->%p, cb_chc_free=%d\n",
612*7c478bd9Sstevel@tonic-gate 		(void *)cbp, cbp->cb_chc_free));
613*7c478bd9Sstevel@tonic-gate 
614*7c478bd9Sstevel@tonic-gate 	mutex_enter(cbp->cb_lock);
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_chc_free) {
617*7c478bd9Sstevel@tonic-gate 		cbp->cb_chc_free--;
618*7c478bd9Sstevel@tonic-gate 		cbch = cbp->cb_chc[ cbp->cb_chc_free ];
619*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
620*7c478bd9Sstevel@tonic-gate 			"rfs4_cb_getch: cb_chc_free=%d ch->%p\n",
621*7c478bd9Sstevel@tonic-gate 			cbp->cb_chc_free, (void *)cbch));
622*7c478bd9Sstevel@tonic-gate 		mutex_exit(cbp->cb_lock);
623*7c478bd9Sstevel@tonic-gate 		(void) CLNT_CONTROL(cbch, CLSET_XID, (char *)&zilch);
624*7c478bd9Sstevel@tonic-gate 		return (cbch);
625*7c478bd9Sstevel@tonic-gate 	}
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate 	mutex_exit(cbp->cb_lock);
628*7c478bd9Sstevel@tonic-gate 
629*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
630*7c478bd9Sstevel@tonic-gate 		"rfs4_cb_getch: calling rfs4_cbch_init\n"));
631*7c478bd9Sstevel@tonic-gate 
632*7c478bd9Sstevel@tonic-gate 	/* none free so make it now */
633*7c478bd9Sstevel@tonic-gate 	cbch = rfs4_cbch_init(cbp);
634*7c478bd9Sstevel@tonic-gate 
635*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
636*7c478bd9Sstevel@tonic-gate 		"rfs4_cb_getch: returning cbch->%p\n", (void *)cbch));
637*7c478bd9Sstevel@tonic-gate 
638*7c478bd9Sstevel@tonic-gate 	return (cbch);
639*7c478bd9Sstevel@tonic-gate }
640*7c478bd9Sstevel@tonic-gate 
641*7c478bd9Sstevel@tonic-gate /*
642*7c478bd9Sstevel@tonic-gate  * Return the client handle to the small cache or
643*7c478bd9Sstevel@tonic-gate  * destroy it.
644*7c478bd9Sstevel@tonic-gate  */
645*7c478bd9Sstevel@tonic-gate static void
646*7c478bd9Sstevel@tonic-gate rfs4_cb_freech(rfs4_cbinfo_t *cbp, CLIENT *ch, bool_t lockheld)
647*7c478bd9Sstevel@tonic-gate {
648*7c478bd9Sstevel@tonic-gate 	if (lockheld == FALSE)
649*7c478bd9Sstevel@tonic-gate 		mutex_enter(cbp->cb_lock);
650*7c478bd9Sstevel@tonic-gate 
651*7c478bd9Sstevel@tonic-gate 	if (cbp->cb_chc_free < RFS4_CBCH_MAX) {
652*7c478bd9Sstevel@tonic-gate 		cbp->cb_chc[ cbp->cb_chc_free++ ] = ch;
653*7c478bd9Sstevel@tonic-gate 		if (lockheld == FALSE)
654*7c478bd9Sstevel@tonic-gate 			mutex_exit(cbp->cb_lock);
655*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
656*7c478bd9Sstevel@tonic-gate 		    "rfs4_cb_freech: caching cbp->%p, ch->%p, cb_chc_free=%d\n",
657*7c478bd9Sstevel@tonic-gate 			(void *)cbp, (void *)ch, cbp->cb_chc_free));
658*7c478bd9Sstevel@tonic-gate 		return;
659*7c478bd9Sstevel@tonic-gate 	}
660*7c478bd9Sstevel@tonic-gate 	if (lockheld == FALSE)
661*7c478bd9Sstevel@tonic-gate 		mutex_exit(cbp->cb_lock);
662*7c478bd9Sstevel@tonic-gate 
663*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
664*7c478bd9Sstevel@tonic-gate 		"rfs4_cb_freech: destroying cbp->%p, ch->%p, cb_chc_free=%d\n",
665*7c478bd9Sstevel@tonic-gate 		(void *)cbp, (void *)ch, cbp->cb_chc_free));
666*7c478bd9Sstevel@tonic-gate 
667*7c478bd9Sstevel@tonic-gate 	/*
668*7c478bd9Sstevel@tonic-gate 	 * cache maxed out of free entries, obliterate
669*7c478bd9Sstevel@tonic-gate 	 * this client handle, destroy it, throw it away.
670*7c478bd9Sstevel@tonic-gate 	 */
671*7c478bd9Sstevel@tonic-gate 	if (ch->cl_auth)
672*7c478bd9Sstevel@tonic-gate 		auth_destroy(ch->cl_auth);
673*7c478bd9Sstevel@tonic-gate 	clnt_destroy(ch);
674*7c478bd9Sstevel@tonic-gate }
675*7c478bd9Sstevel@tonic-gate 
676*7c478bd9Sstevel@tonic-gate /*
677*7c478bd9Sstevel@tonic-gate  * With the supplied callback information - initialize the client
678*7c478bd9Sstevel@tonic-gate  * callback data.  If there is a callback in progress, save the
679*7c478bd9Sstevel@tonic-gate  * callback info so that a thread can pick it up in the future.
680*7c478bd9Sstevel@tonic-gate  */
681*7c478bd9Sstevel@tonic-gate void
682*7c478bd9Sstevel@tonic-gate rfs4_client_setcb(rfs4_client_t *cp, cb_client4 *cb, uint32_t cb_ident)
683*7c478bd9Sstevel@tonic-gate {
684*7c478bd9Sstevel@tonic-gate 	char *addr = NULL;
685*7c478bd9Sstevel@tonic-gate 	char *netid = NULL;
686*7c478bd9Sstevel@tonic-gate 	rfs4_cbinfo_t *cbp = &cp->cbinfo;
687*7c478bd9Sstevel@tonic-gate 	size_t len;
688*7c478bd9Sstevel@tonic-gate 
689*7c478bd9Sstevel@tonic-gate 	/* Set the call back for the client */
690*7c478bd9Sstevel@tonic-gate 	if (cb->cb_location.r_addr && cb->cb_location.r_netid) {
691*7c478bd9Sstevel@tonic-gate 		len = strlen(cb->cb_location.r_addr) + 1;
692*7c478bd9Sstevel@tonic-gate 		addr = kmem_alloc(len, KM_SLEEP);
693*7c478bd9Sstevel@tonic-gate 		bcopy(cb->cb_location.r_addr, addr, len);
694*7c478bd9Sstevel@tonic-gate 		len = strlen(cb->cb_location.r_netid) + 1;
695*7c478bd9Sstevel@tonic-gate 		netid = kmem_alloc(len, KM_SLEEP);
696*7c478bd9Sstevel@tonic-gate 		bcopy(cb->cb_location.r_netid, netid, len);
697*7c478bd9Sstevel@tonic-gate 	}
698*7c478bd9Sstevel@tonic-gate 	/* ready to save the new information but first free old, if exists */
699*7c478bd9Sstevel@tonic-gate 	mutex_enter(cbp->cb_lock);
700*7c478bd9Sstevel@tonic-gate 
701*7c478bd9Sstevel@tonic-gate 	cbp->cb_newer.cb_callback.cb_program = cb->cb_program;
702*7c478bd9Sstevel@tonic-gate 	cbp->cb_newer.cb_callback.cb_location.r_addr = addr;
703*7c478bd9Sstevel@tonic-gate 	cbp->cb_newer.cb_callback.cb_location.r_netid = netid;
704*7c478bd9Sstevel@tonic-gate 
705*7c478bd9Sstevel@tonic-gate 	cbp->cb_newer.cb_ident = cb_ident;
706*7c478bd9Sstevel@tonic-gate 
707*7c478bd9Sstevel@tonic-gate 	if (addr && *addr && netid && *netid) {
708*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_new = TRUE;
709*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_confirmed = FALSE;
710*7c478bd9Sstevel@tonic-gate 	} else {
711*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_new = FALSE;
712*7c478bd9Sstevel@tonic-gate 		cbp->cb_newer.cb_confirmed = FALSE;
713*7c478bd9Sstevel@tonic-gate 	}
714*7c478bd9Sstevel@tonic-gate 
715*7c478bd9Sstevel@tonic-gate 	mutex_exit(cbp->cb_lock);
716*7c478bd9Sstevel@tonic-gate }
717*7c478bd9Sstevel@tonic-gate 
718*7c478bd9Sstevel@tonic-gate /*
719*7c478bd9Sstevel@tonic-gate  * The server uses this when processing SETCLIENTID_CONFIRM.  Callback
720*7c478bd9Sstevel@tonic-gate  * information may have been provided on SETCLIENTID and this call
721*7c478bd9Sstevel@tonic-gate  * marks that information as confirmed and then starts a thread to
722*7c478bd9Sstevel@tonic-gate  * test the callback path.
723*7c478bd9Sstevel@tonic-gate  */
724*7c478bd9Sstevel@tonic-gate void
725*7c478bd9Sstevel@tonic-gate rfs4_deleg_cb_check(rfs4_client_t *cp)
726*7c478bd9Sstevel@tonic-gate {
727*7c478bd9Sstevel@tonic-gate 	if (cp->cbinfo.cb_newer.cb_new == FALSE)
728*7c478bd9Sstevel@tonic-gate 		return;
729*7c478bd9Sstevel@tonic-gate 
730*7c478bd9Sstevel@tonic-gate 	cp->cbinfo.cb_newer.cb_confirmed = TRUE;
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_hold(cp->dbe); /* hold the client struct for thread */
733*7c478bd9Sstevel@tonic-gate 
734*7c478bd9Sstevel@tonic-gate 	(void) thread_create(NULL, 0, rfs4_do_cb_null, cp, 0, &p0, TS_RUN,
735*7c478bd9Sstevel@tonic-gate 			    minclsyspri);
736*7c478bd9Sstevel@tonic-gate }
737*7c478bd9Sstevel@tonic-gate 
738*7c478bd9Sstevel@tonic-gate static void
739*7c478bd9Sstevel@tonic-gate rfs4args_cb_recall_free(nfs_cb_argop4 *argop)
740*7c478bd9Sstevel@tonic-gate {
741*7c478bd9Sstevel@tonic-gate 	CB_RECALL4args	*rec_argp;
742*7c478bd9Sstevel@tonic-gate 
743*7c478bd9Sstevel@tonic-gate 	rec_argp = &argop->nfs_cb_argop4_u.opcbrecall;
744*7c478bd9Sstevel@tonic-gate 	if (rec_argp->fh.nfs_fh4_val)
745*7c478bd9Sstevel@tonic-gate 		kmem_free(rec_argp->fh.nfs_fh4_val, rec_argp->fh.nfs_fh4_len);
746*7c478bd9Sstevel@tonic-gate }
747*7c478bd9Sstevel@tonic-gate 
748*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
749*7c478bd9Sstevel@tonic-gate static void
750*7c478bd9Sstevel@tonic-gate rfs4args_cb_getattr_free(nfs_cb_argop4 *argop)
751*7c478bd9Sstevel@tonic-gate {
752*7c478bd9Sstevel@tonic-gate 	CB_GETATTR4args *argp;
753*7c478bd9Sstevel@tonic-gate 
754*7c478bd9Sstevel@tonic-gate 	argp = &argop->nfs_cb_argop4_u.opcbgetattr;
755*7c478bd9Sstevel@tonic-gate 	if (argp->fh.nfs_fh4_val)
756*7c478bd9Sstevel@tonic-gate 		kmem_free(argp->fh.nfs_fh4_val, argp->fh.nfs_fh4_len);
757*7c478bd9Sstevel@tonic-gate }
758*7c478bd9Sstevel@tonic-gate 
759*7c478bd9Sstevel@tonic-gate static void
760*7c478bd9Sstevel@tonic-gate rfs4freeargres(CB_COMPOUND4args *args, CB_COMPOUND4res *resp)
761*7c478bd9Sstevel@tonic-gate {
762*7c478bd9Sstevel@tonic-gate 	int i, arglen;
763*7c478bd9Sstevel@tonic-gate 	nfs_cb_argop4 *argop;
764*7c478bd9Sstevel@tonic-gate 
765*7c478bd9Sstevel@tonic-gate 	/*
766*7c478bd9Sstevel@tonic-gate 	 * First free any special args alloc'd for specific ops.
767*7c478bd9Sstevel@tonic-gate 	 */
768*7c478bd9Sstevel@tonic-gate 	arglen = args->array_len;
769*7c478bd9Sstevel@tonic-gate 	argop = args->array;
770*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < arglen; i++, argop++) {
771*7c478bd9Sstevel@tonic-gate 
772*7c478bd9Sstevel@tonic-gate 		switch (argop->argop) {
773*7c478bd9Sstevel@tonic-gate 		case OP_CB_RECALL:
774*7c478bd9Sstevel@tonic-gate 			rfs4args_cb_recall_free(argop);
775*7c478bd9Sstevel@tonic-gate 			break;
776*7c478bd9Sstevel@tonic-gate 
777*7c478bd9Sstevel@tonic-gate 		case OP_CB_GETATTR:
778*7c478bd9Sstevel@tonic-gate 			rfs4args_cb_getattr_free(argop);
779*7c478bd9Sstevel@tonic-gate 			break;
780*7c478bd9Sstevel@tonic-gate 
781*7c478bd9Sstevel@tonic-gate 		default:
782*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_deleg_debug, (CE_NOTE,
783*7c478bd9Sstevel@tonic-gate 				"rfs4freeargres: unknown op"));
784*7c478bd9Sstevel@tonic-gate 			return;
785*7c478bd9Sstevel@tonic-gate 		}
786*7c478bd9Sstevel@tonic-gate 	}
787*7c478bd9Sstevel@tonic-gate 
788*7c478bd9Sstevel@tonic-gate 	if (args->tag.utf8string_len > 0)
789*7c478bd9Sstevel@tonic-gate 		UTF8STRING_FREE(args->tag)
790*7c478bd9Sstevel@tonic-gate 
791*7c478bd9Sstevel@tonic-gate 	kmem_free(args->array, arglen * sizeof (nfs_cb_argop4));
792*7c478bd9Sstevel@tonic-gate 	if (resp)
793*7c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_CB_COMPOUND4res, (caddr_t)resp);
794*7c478bd9Sstevel@tonic-gate }
795*7c478bd9Sstevel@tonic-gate 
796*7c478bd9Sstevel@tonic-gate /*
797*7c478bd9Sstevel@tonic-gate  * General callback routine for the server to the client.
798*7c478bd9Sstevel@tonic-gate  */
799*7c478bd9Sstevel@tonic-gate static enum clnt_stat
800*7c478bd9Sstevel@tonic-gate rfs4_do_callback(rfs4_client_t	*cp, CB_COMPOUND4args *args,
801*7c478bd9Sstevel@tonic-gate 		CB_COMPOUND4res *res, struct timeval timeout)
802*7c478bd9Sstevel@tonic-gate {
803*7c478bd9Sstevel@tonic-gate 	rfs4_cbinfo_t *cbp;
804*7c478bd9Sstevel@tonic-gate 	CLIENT *ch;
805*7c478bd9Sstevel@tonic-gate 	/* start with this in case cb_getch() fails */
806*7c478bd9Sstevel@tonic-gate 	enum clnt_stat	stat = RPC_FAILED;
807*7c478bd9Sstevel@tonic-gate 
808*7c478bd9Sstevel@tonic-gate 	res->tag.utf8string_val = NULL;
809*7c478bd9Sstevel@tonic-gate 	res->array = NULL;
810*7c478bd9Sstevel@tonic-gate 
811*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
812*7c478bd9Sstevel@tonic-gate 		"rfs4_do_callback: enter cp->%p\n", (void *)cp));
813*7c478bd9Sstevel@tonic-gate 
814*7c478bd9Sstevel@tonic-gate retry:
815*7c478bd9Sstevel@tonic-gate 	cbp = rfs4_cbinfo_hold(cp);
816*7c478bd9Sstevel@tonic-gate 	if (cbp == NULL)
817*7c478bd9Sstevel@tonic-gate 		return (stat);
818*7c478bd9Sstevel@tonic-gate 
819*7c478bd9Sstevel@tonic-gate 	/* get a client handle */
820*7c478bd9Sstevel@tonic-gate 	if ((ch = rfs4_cb_getch(cbp)) != NULL) {
821*7c478bd9Sstevel@tonic-gate 		/*
822*7c478bd9Sstevel@tonic-gate 		 * reset the cb_ident since it may have changed in
823*7c478bd9Sstevel@tonic-gate 		 * rfs4_cbinfo_hold()
824*7c478bd9Sstevel@tonic-gate 		 */
825*7c478bd9Sstevel@tonic-gate 		args->callback_ident = cbp->cb_ident;
826*7c478bd9Sstevel@tonic-gate 
827*7c478bd9Sstevel@tonic-gate 		stat = clnt_call(ch, CB_COMPOUND, xdr_CB_COMPOUND4args,
828*7c478bd9Sstevel@tonic-gate 			(caddr_t)args, xdr_CB_COMPOUND4res,
829*7c478bd9Sstevel@tonic-gate 			(caddr_t)res, timeout);
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate 		/* free client handle */
832*7c478bd9Sstevel@tonic-gate 		rfs4_cb_freech(cbp, ch, FALSE);
833*7c478bd9Sstevel@tonic-gate 	}
834*7c478bd9Sstevel@tonic-gate 
835*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_cb_debug, (CE_NOTE,
836*7c478bd9Sstevel@tonic-gate 		"rfs4_do_callback: exit with RPC status %d, %s",
837*7c478bd9Sstevel@tonic-gate 		stat, clnt_sperrno(stat)));
838*7c478bd9Sstevel@tonic-gate 
839*7c478bd9Sstevel@tonic-gate 	/*
840*7c478bd9Sstevel@tonic-gate 	 * If the rele says that there may be new callback info then
841*7c478bd9Sstevel@tonic-gate 	 * retry this sequence and it may succeed as a result of the
842*7c478bd9Sstevel@tonic-gate 	 * new callback path
843*7c478bd9Sstevel@tonic-gate 	 */
844*7c478bd9Sstevel@tonic-gate 	if (rfs4_cbinfo_rele(cbp,
845*7c478bd9Sstevel@tonic-gate 		(stat == RPC_SUCCESS ? CB_NOCHANGE : CB_FAILED)) == TRUE)
846*7c478bd9Sstevel@tonic-gate 		goto retry;
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate 	return (stat);
849*7c478bd9Sstevel@tonic-gate }
850*7c478bd9Sstevel@tonic-gate 
851*7c478bd9Sstevel@tonic-gate /*
852*7c478bd9Sstevel@tonic-gate  * Used by the NFSv4 server to get attributes for a file while
853*7c478bd9Sstevel@tonic-gate  * handling the case where a file has been write delegated.  For the
854*7c478bd9Sstevel@tonic-gate  * time being, VOP_GETATTR() is called and CB_GETATTR processing is
855*7c478bd9Sstevel@tonic-gate  * not undertaken.  This call site is maintained in case the server is
856*7c478bd9Sstevel@tonic-gate  * updated in the future to handle write delegation space guarantees.
857*7c478bd9Sstevel@tonic-gate  */
858*7c478bd9Sstevel@tonic-gate nfsstat4
859*7c478bd9Sstevel@tonic-gate rfs4_vop_getattr(vnode_t *vp, vattr_t *vap, int flag, cred_t *cr)
860*7c478bd9Sstevel@tonic-gate {
861*7c478bd9Sstevel@tonic-gate 	uint_t mask;
862*7c478bd9Sstevel@tonic-gate 	int error;
863*7c478bd9Sstevel@tonic-gate 
864*7c478bd9Sstevel@tonic-gate 	mask = vap->va_mask;
865*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(vp, vap, flag, cr);
866*7c478bd9Sstevel@tonic-gate 	/*
867*7c478bd9Sstevel@tonic-gate 	 * Some file systems clobber va_mask. it is probably wrong of
868*7c478bd9Sstevel@tonic-gate 	 * them to do so, nonethless we practice defensive coding.
869*7c478bd9Sstevel@tonic-gate 	 * See bug id 4276830.
870*7c478bd9Sstevel@tonic-gate 	 */
871*7c478bd9Sstevel@tonic-gate 	vap->va_mask = mask;
872*7c478bd9Sstevel@tonic-gate 	return (puterrno4(error));
873*7c478bd9Sstevel@tonic-gate }
874*7c478bd9Sstevel@tonic-gate 
875*7c478bd9Sstevel@tonic-gate /*
876*7c478bd9Sstevel@tonic-gate  * This is used everywhere in the v2/v3 server to allow the
877*7c478bd9Sstevel@tonic-gate  * integration of all NFS versions and the support of delegation.  For
878*7c478bd9Sstevel@tonic-gate  * now, just call the VOP_GETATTR().  If the NFSv4 server is enhanced
879*7c478bd9Sstevel@tonic-gate  * in the future to provide space guarantees for write delegations
880*7c478bd9Sstevel@tonic-gate  * then this call site should be expanded to interact with the client.
881*7c478bd9Sstevel@tonic-gate  */
882*7c478bd9Sstevel@tonic-gate int
883*7c478bd9Sstevel@tonic-gate rfs4_delegated_getattr(vnode_t *vp, vattr_t *vap, int flag, cred_t *cr)
884*7c478bd9Sstevel@tonic-gate {
885*7c478bd9Sstevel@tonic-gate 	return (VOP_GETATTR(vp, vap, flag, cr));
886*7c478bd9Sstevel@tonic-gate }
887*7c478bd9Sstevel@tonic-gate 
888*7c478bd9Sstevel@tonic-gate /*
889*7c478bd9Sstevel@tonic-gate  * Place the actual cb_recall otw call to client.
890*7c478bd9Sstevel@tonic-gate  */
891*7c478bd9Sstevel@tonic-gate static void
892*7c478bd9Sstevel@tonic-gate rfs4_do_cb_recall(rfs4_deleg_state_t *dsp, bool_t trunc)
893*7c478bd9Sstevel@tonic-gate {
894*7c478bd9Sstevel@tonic-gate 	CB_COMPOUND4args	cb4_args;
895*7c478bd9Sstevel@tonic-gate 	CB_COMPOUND4res		cb4_res;
896*7c478bd9Sstevel@tonic-gate 	CB_RECALL4args		*rec_argp;
897*7c478bd9Sstevel@tonic-gate 	nfs_cb_argop4		*argop;
898*7c478bd9Sstevel@tonic-gate 	int			numops;
899*7c478bd9Sstevel@tonic-gate 	int			argoplist_size;
900*7c478bd9Sstevel@tonic-gate 	struct timeval		timeout;
901*7c478bd9Sstevel@tonic-gate 	nfs_fh4			*fhp;
902*7c478bd9Sstevel@tonic-gate 	enum clnt_stat		call_stat;
903*7c478bd9Sstevel@tonic-gate 
904*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_deleg_debug, (CE_NOTE, "rfs4_do_cb_recall: enter"));
905*7c478bd9Sstevel@tonic-gate 	/*
906*7c478bd9Sstevel@tonic-gate 	 * set up the compound args
907*7c478bd9Sstevel@tonic-gate 	 */
908*7c478bd9Sstevel@tonic-gate 	numops = 1;	/* CB_RECALL only */
909*7c478bd9Sstevel@tonic-gate 
910*7c478bd9Sstevel@tonic-gate 	argoplist_size = numops * sizeof (nfs_cb_argop4);
911*7c478bd9Sstevel@tonic-gate 	argop = kmem_zalloc(argoplist_size, KM_SLEEP);
912*7c478bd9Sstevel@tonic-gate 	argop->argop = OP_CB_RECALL;
913*7c478bd9Sstevel@tonic-gate 	rec_argp = &argop->nfs_cb_argop4_u.opcbrecall;
914*7c478bd9Sstevel@tonic-gate 
915*7c478bd9Sstevel@tonic-gate 	(void) str_to_utf8("cb_recall", &cb4_args.tag);
916*7c478bd9Sstevel@tonic-gate 	cb4_args.minorversion = CB4_MINORVERSION;
917*7c478bd9Sstevel@tonic-gate 	/* cb4_args.callback_ident is set in rfs4_do_callback() */
918*7c478bd9Sstevel@tonic-gate 	cb4_args.array_len = numops;
919*7c478bd9Sstevel@tonic-gate 	cb4_args.array = argop;
920*7c478bd9Sstevel@tonic-gate 
921*7c478bd9Sstevel@tonic-gate 	/*
922*7c478bd9Sstevel@tonic-gate 	 * fill in the args struct
923*7c478bd9Sstevel@tonic-gate 	 */
924*7c478bd9Sstevel@tonic-gate 	bcopy(&dsp->delegid.stateid, &rec_argp->stateid, sizeof (stateid4));
925*7c478bd9Sstevel@tonic-gate 	rec_argp->truncate = trunc;
926*7c478bd9Sstevel@tonic-gate 
927*7c478bd9Sstevel@tonic-gate 	fhp = &dsp->finfo->filehandle;
928*7c478bd9Sstevel@tonic-gate 	rec_argp->fh.nfs_fh4_val = kmem_alloc(sizeof (char) *
929*7c478bd9Sstevel@tonic-gate 					    fhp->nfs_fh4_len, KM_SLEEP);
930*7c478bd9Sstevel@tonic-gate 	nfs_fh4_copy(fhp, &rec_argp->fh);
931*7c478bd9Sstevel@tonic-gate 
932*7c478bd9Sstevel@tonic-gate 	/* Keep track of when we did this for observability */
933*7c478bd9Sstevel@tonic-gate 	dsp->time_recalled = gethrestime_sec();
934*7c478bd9Sstevel@tonic-gate 
935*7c478bd9Sstevel@tonic-gate 	/*
936*7c478bd9Sstevel@tonic-gate 	 * Set up the timeout for the callback and make the actual call.
937*7c478bd9Sstevel@tonic-gate 	 * Timeout will be 80% of the lease period for this server.
938*7c478bd9Sstevel@tonic-gate 	 */
939*7c478bd9Sstevel@tonic-gate 	timeout.tv_sec = (rfs4_lease_time * 80) / 100;
940*7c478bd9Sstevel@tonic-gate 	timeout.tv_usec = 0;
941*7c478bd9Sstevel@tonic-gate 
942*7c478bd9Sstevel@tonic-gate 	call_stat = rfs4_do_callback(dsp->client, &cb4_args,
943*7c478bd9Sstevel@tonic-gate 		&cb4_res, timeout);
944*7c478bd9Sstevel@tonic-gate 
945*7c478bd9Sstevel@tonic-gate 	if (call_stat != RPC_SUCCESS || cb4_res.status != NFS4_OK) {
946*7c478bd9Sstevel@tonic-gate 		rfs4_revoke_deleg(dsp);
947*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_deleg_debug, (CE_NOTE,
948*7c478bd9Sstevel@tonic-gate 			"rfs4_do_cb_recall: rpcstat=%d cbstat=%d ",
949*7c478bd9Sstevel@tonic-gate 			call_stat, cb4_res.status));
950*7c478bd9Sstevel@tonic-gate 	}
951*7c478bd9Sstevel@tonic-gate 
952*7c478bd9Sstevel@tonic-gate 	rfs4freeargres(&cb4_args, &cb4_res);
953*7c478bd9Sstevel@tonic-gate }
954*7c478bd9Sstevel@tonic-gate 
955*7c478bd9Sstevel@tonic-gate struct recall_arg {
956*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
957*7c478bd9Sstevel@tonic-gate 	void (*recall)(rfs4_deleg_state_t *, bool_t trunc);
958*7c478bd9Sstevel@tonic-gate 	bool_t trunc;
959*7c478bd9Sstevel@tonic-gate };
960*7c478bd9Sstevel@tonic-gate 
961*7c478bd9Sstevel@tonic-gate static void
962*7c478bd9Sstevel@tonic-gate do_recall(struct recall_arg *arg)
963*7c478bd9Sstevel@tonic-gate {
964*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp = arg->dsp;
965*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = dsp->finfo;
966*7c478bd9Sstevel@tonic-gate 	callb_cpr_t cpr_info;
967*7c478bd9Sstevel@tonic-gate 	kmutex_t cpr_lock;
968*7c478bd9Sstevel@tonic-gate 
969*7c478bd9Sstevel@tonic-gate 	mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL);
970*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_INIT(&cpr_info, &cpr_lock, callb_generic_cpr, "nfsv4Recall");
971*7c478bd9Sstevel@tonic-gate 
972*7c478bd9Sstevel@tonic-gate 	/*
973*7c478bd9Sstevel@tonic-gate 	 * It is possible that before this thread starts
974*7c478bd9Sstevel@tonic-gate 	 * the client has send us a return_delegation, and
975*7c478bd9Sstevel@tonic-gate 	 * if that is the case we do not need to send the
976*7c478bd9Sstevel@tonic-gate 	 * recall callback.
977*7c478bd9Sstevel@tonic-gate 	 */
978*7c478bd9Sstevel@tonic-gate 	if (dsp->dtype != OPEN_DELEGATE_NONE) {
979*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_deleg_debug,
980*7c478bd9Sstevel@tonic-gate 		    (CE_NOTE, "recall = %p, state = %p, fp = %p trunc = %d",
981*7c478bd9Sstevel@tonic-gate 		    (void*)arg->recall, (void*)dsp, (void*)fp, arg->trunc));
982*7c478bd9Sstevel@tonic-gate 
983*7c478bd9Sstevel@tonic-gate 		if (arg->recall)
984*7c478bd9Sstevel@tonic-gate 			(void) (*arg->recall)(dsp, arg->trunc);
985*7c478bd9Sstevel@tonic-gate 	}
986*7c478bd9Sstevel@tonic-gate 
987*7c478bd9Sstevel@tonic-gate 	mutex_enter(fp->dinfo->recall_lock);
988*7c478bd9Sstevel@tonic-gate 	/*
989*7c478bd9Sstevel@tonic-gate 	 * Recall count may go negative if the parent thread that is
990*7c478bd9Sstevel@tonic-gate 	 * creating the individual callback threads does not modify
991*7c478bd9Sstevel@tonic-gate 	 * the recall_count field before the callback thread actually
992*7c478bd9Sstevel@tonic-gate 	 * gets a response from the CB_RECALL
993*7c478bd9Sstevel@tonic-gate 	 */
994*7c478bd9Sstevel@tonic-gate 	fp->dinfo->recall_count--;
995*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->recall_count == 0)
996*7c478bd9Sstevel@tonic-gate 		cv_signal(fp->dinfo->recall_cv);
997*7c478bd9Sstevel@tonic-gate 	mutex_exit(fp->dinfo->recall_lock);
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate 	mutex_enter(&cpr_lock);
1000*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cpr_info);
1001*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&cpr_lock);
1002*7c478bd9Sstevel@tonic-gate 
1003*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_rele(dsp); /* release the hold for this thread */
1004*7c478bd9Sstevel@tonic-gate 
1005*7c478bd9Sstevel@tonic-gate 	kmem_free(arg, sizeof (struct recall_arg));
1006*7c478bd9Sstevel@tonic-gate }
1007*7c478bd9Sstevel@tonic-gate 
1008*7c478bd9Sstevel@tonic-gate struct master_recall_args {
1009*7c478bd9Sstevel@tonic-gate     rfs4_file_t *fp;
1010*7c478bd9Sstevel@tonic-gate     void (*recall)(rfs4_deleg_state_t *, bool_t);
1011*7c478bd9Sstevel@tonic-gate     bool_t trunc;
1012*7c478bd9Sstevel@tonic-gate };
1013*7c478bd9Sstevel@tonic-gate 
1014*7c478bd9Sstevel@tonic-gate static void
1015*7c478bd9Sstevel@tonic-gate do_recall_file(struct master_recall_args *map)
1016*7c478bd9Sstevel@tonic-gate {
1017*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = map->fp;
1018*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
1019*7c478bd9Sstevel@tonic-gate 	struct recall_arg *arg;
1020*7c478bd9Sstevel@tonic-gate 	callb_cpr_t cpr_info;
1021*7c478bd9Sstevel@tonic-gate 	kmutex_t cpr_lock;
1022*7c478bd9Sstevel@tonic-gate 	int32_t recall_count;
1023*7c478bd9Sstevel@tonic-gate 
1024*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(fp->dbe);
1025*7c478bd9Sstevel@tonic-gate 	/* Recall already in progress */
1026*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->recall_count != 0) {
1027*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_rele_nolock(fp->dbe);
1028*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(fp->dbe);
1029*7c478bd9Sstevel@tonic-gate 		kmem_free(map, sizeof (struct master_recall_args));
1030*7c478bd9Sstevel@tonic-gate 		return;
1031*7c478bd9Sstevel@tonic-gate 	}
1032*7c478bd9Sstevel@tonic-gate 
1033*7c478bd9Sstevel@tonic-gate 	mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL);
1034*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_INIT(&cpr_info, &cpr_lock, callb_generic_cpr,
1035*7c478bd9Sstevel@tonic-gate 			"nfsv4RecallFile");
1036*7c478bd9Sstevel@tonic-gate 
1037*7c478bd9Sstevel@tonic-gate 	recall_count = 0;
1038*7c478bd9Sstevel@tonic-gate 	for (dsp = fp->delegationlist.next->dsp; dsp != NULL;
1039*7c478bd9Sstevel@tonic-gate 		dsp = dsp->delegationlist.next->dsp) {
1040*7c478bd9Sstevel@tonic-gate 		arg = kmem_alloc(sizeof (struct recall_arg), KM_SLEEP);
1041*7c478bd9Sstevel@tonic-gate 		arg->recall = map->recall;
1042*7c478bd9Sstevel@tonic-gate 		arg->trunc = map->trunc;
1043*7c478bd9Sstevel@tonic-gate 
1044*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_hold(dsp->dbe);	/* hold for receiving thread */
1045*7c478bd9Sstevel@tonic-gate 
1046*7c478bd9Sstevel@tonic-gate 		arg->dsp = dsp;
1047*7c478bd9Sstevel@tonic-gate 
1048*7c478bd9Sstevel@tonic-gate 		recall_count++;
1049*7c478bd9Sstevel@tonic-gate 
1050*7c478bd9Sstevel@tonic-gate 		(void) thread_create(NULL, 0, do_recall, arg, 0, &p0, TS_RUN,
1051*7c478bd9Sstevel@tonic-gate 				    minclsyspri);
1052*7c478bd9Sstevel@tonic-gate 	}
1053*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(fp->dbe);
1054*7c478bd9Sstevel@tonic-gate 
1055*7c478bd9Sstevel@tonic-gate 	mutex_enter(fp->dinfo->recall_lock);
1056*7c478bd9Sstevel@tonic-gate 	/*
1057*7c478bd9Sstevel@tonic-gate 	 * Recall count may go negative if the parent thread that is
1058*7c478bd9Sstevel@tonic-gate 	 * creating the individual callback threads does not modify
1059*7c478bd9Sstevel@tonic-gate 	 * the recall_count field before the callback thread actually
1060*7c478bd9Sstevel@tonic-gate 	 * gets a response from the CB_RECALL
1061*7c478bd9Sstevel@tonic-gate 	 */
1062*7c478bd9Sstevel@tonic-gate 	fp->dinfo->recall_count += recall_count;
1063*7c478bd9Sstevel@tonic-gate 	while (fp->dinfo->recall_count)
1064*7c478bd9Sstevel@tonic-gate 		cv_wait(fp->dinfo->recall_cv, fp->dinfo->recall_lock);
1065*7c478bd9Sstevel@tonic-gate 
1066*7c478bd9Sstevel@tonic-gate 	mutex_exit(fp->dinfo->recall_lock);
1067*7c478bd9Sstevel@tonic-gate 
1068*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_deleg_debug, (CE_NOTE, "Recall complete for %p",
1069*7c478bd9Sstevel@tonic-gate 		    (void*)fp));
1070*7c478bd9Sstevel@tonic-gate 	rfs4_file_rele(fp);
1071*7c478bd9Sstevel@tonic-gate 	kmem_free(map, sizeof (struct master_recall_args));
1072*7c478bd9Sstevel@tonic-gate 	mutex_enter(&cpr_lock);
1073*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cpr_info);
1074*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&cpr_lock);
1075*7c478bd9Sstevel@tonic-gate }
1076*7c478bd9Sstevel@tonic-gate 
1077*7c478bd9Sstevel@tonic-gate static void
1078*7c478bd9Sstevel@tonic-gate rfs4_recall_file(rfs4_file_t *fp,
1079*7c478bd9Sstevel@tonic-gate 	void (*recall)(rfs4_deleg_state_t *, bool_t trunc),
1080*7c478bd9Sstevel@tonic-gate 	bool_t trunc, rfs4_client_t *cp)
1081*7c478bd9Sstevel@tonic-gate {
1082*7c478bd9Sstevel@tonic-gate 	struct master_recall_args *args;
1083*7c478bd9Sstevel@tonic-gate 
1084*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(fp->dbe);
1085*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->dtype == OPEN_DELEGATE_NONE) {
1086*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(fp->dbe);
1087*7c478bd9Sstevel@tonic-gate 		return;
1088*7c478bd9Sstevel@tonic-gate 	}
1089*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_hold(fp->dbe);	/* hold for new thread */
1090*7c478bd9Sstevel@tonic-gate 
1091*7c478bd9Sstevel@tonic-gate 	/*
1092*7c478bd9Sstevel@tonic-gate 	 * Mark the time we started the recall processing.
1093*7c478bd9Sstevel@tonic-gate 	 * If it has been previously recalled, do not reset the
1094*7c478bd9Sstevel@tonic-gate 	 * timer since this is used for the revocation decision.
1095*7c478bd9Sstevel@tonic-gate 	 */
1096*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->time_recalled == 0)
1097*7c478bd9Sstevel@tonic-gate 		fp->dinfo->time_recalled = gethrestime_sec();
1098*7c478bd9Sstevel@tonic-gate 	fp->dinfo->ever_recalled = TRUE; /* used for policy decision */
1099*7c478bd9Sstevel@tonic-gate 	/* Client causing recall not always available */
1100*7c478bd9Sstevel@tonic-gate 	if (cp)
1101*7c478bd9Sstevel@tonic-gate 		fp->dinfo->conflicted_client = cp->clientid;
1102*7c478bd9Sstevel@tonic-gate 
1103*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(fp->dbe);
1104*7c478bd9Sstevel@tonic-gate 
1105*7c478bd9Sstevel@tonic-gate 	args = kmem_alloc(sizeof (struct master_recall_args), KM_SLEEP);
1106*7c478bd9Sstevel@tonic-gate 	args->fp = fp;
1107*7c478bd9Sstevel@tonic-gate 	args->recall = recall;
1108*7c478bd9Sstevel@tonic-gate 	args->trunc = trunc;
1109*7c478bd9Sstevel@tonic-gate 
1110*7c478bd9Sstevel@tonic-gate 	(void) thread_create(NULL, 0, do_recall_file, args, 0, &p0, TS_RUN,
1111*7c478bd9Sstevel@tonic-gate 			    minclsyspri);
1112*7c478bd9Sstevel@tonic-gate }
1113*7c478bd9Sstevel@tonic-gate 
1114*7c478bd9Sstevel@tonic-gate void
1115*7c478bd9Sstevel@tonic-gate rfs4_recall_deleg(rfs4_file_t *fp, bool_t trunc, rfs4_client_t *cp)
1116*7c478bd9Sstevel@tonic-gate {
1117*7c478bd9Sstevel@tonic-gate 	time_t elapsed1, elapsed2;
1118*7c478bd9Sstevel@tonic-gate 
1119*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->time_recalled != 0) {
1120*7c478bd9Sstevel@tonic-gate 		elapsed1 = gethrestime_sec() - fp->dinfo->time_recalled;
1121*7c478bd9Sstevel@tonic-gate 		elapsed2 = gethrestime_sec() - fp->dinfo->time_lastwrite;
1122*7c478bd9Sstevel@tonic-gate 		/* First check to see if a revocation should occur */
1123*7c478bd9Sstevel@tonic-gate 		if (elapsed1 > rfs4_lease_time &&
1124*7c478bd9Sstevel@tonic-gate 			elapsed2 > rfs4_lease_time) {
1125*7c478bd9Sstevel@tonic-gate 			rfs4_revoke_file(fp);
1126*7c478bd9Sstevel@tonic-gate 			return;
1127*7c478bd9Sstevel@tonic-gate 		}
1128*7c478bd9Sstevel@tonic-gate 		/*
1129*7c478bd9Sstevel@tonic-gate 		 * Next check to see if a recall should be done again
1130*7c478bd9Sstevel@tonic-gate 		 * so quickly.
1131*7c478bd9Sstevel@tonic-gate 		 */
1132*7c478bd9Sstevel@tonic-gate 		if (elapsed1 <= ((rfs4_lease_time * 20) / 100))
1133*7c478bd9Sstevel@tonic-gate 			return;
1134*7c478bd9Sstevel@tonic-gate 	}
1135*7c478bd9Sstevel@tonic-gate 	rfs4_recall_file(fp, rfs4_do_cb_recall, trunc, cp);
1136*7c478bd9Sstevel@tonic-gate }
1137*7c478bd9Sstevel@tonic-gate 
1138*7c478bd9Sstevel@tonic-gate /*
1139*7c478bd9Sstevel@tonic-gate  * rfs4_check_recall is called from rfs4_do_open to determine if the current
1140*7c478bd9Sstevel@tonic-gate  * open conflicts with the delegation.
1141*7c478bd9Sstevel@tonic-gate  * Return true if we need recall otherwise false.
1142*7c478bd9Sstevel@tonic-gate  * Assumes entry locks for sp and sp->finfo are held.
1143*7c478bd9Sstevel@tonic-gate  */
1144*7c478bd9Sstevel@tonic-gate bool_t
1145*7c478bd9Sstevel@tonic-gate rfs4_check_recall(rfs4_state_t *sp, uint32_t access)
1146*7c478bd9Sstevel@tonic-gate {
1147*7c478bd9Sstevel@tonic-gate 	open_delegation_type4 dtype = sp->finfo->dinfo->dtype;
1148*7c478bd9Sstevel@tonic-gate 
1149*7c478bd9Sstevel@tonic-gate 	switch (dtype) {
1150*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_NONE:
1151*7c478bd9Sstevel@tonic-gate 		/* Not currently delegated so there is nothing to do */
1152*7c478bd9Sstevel@tonic-gate 		return (FALSE);
1153*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_READ:
1154*7c478bd9Sstevel@tonic-gate 		/*
1155*7c478bd9Sstevel@tonic-gate 		 * If the access is only asking for READ then there is
1156*7c478bd9Sstevel@tonic-gate 		 * no conflict and nothing to do.  If it is asking
1157*7c478bd9Sstevel@tonic-gate 		 * for write, then there will be conflict and the read
1158*7c478bd9Sstevel@tonic-gate 		 * delegation should be recalled.
1159*7c478bd9Sstevel@tonic-gate 		 */
1160*7c478bd9Sstevel@tonic-gate 		if (access == OPEN4_SHARE_ACCESS_READ)
1161*7c478bd9Sstevel@tonic-gate 			return (FALSE);
1162*7c478bd9Sstevel@tonic-gate 		else
1163*7c478bd9Sstevel@tonic-gate 			return (TRUE);
1164*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_WRITE:
1165*7c478bd9Sstevel@tonic-gate 		/* Check to see if this client has the delegation */
1166*7c478bd9Sstevel@tonic-gate 		return (rfs4_is_deleg(sp));
1167*7c478bd9Sstevel@tonic-gate 	}
1168*7c478bd9Sstevel@tonic-gate 
1169*7c478bd9Sstevel@tonic-gate 	return (FALSE);
1170*7c478bd9Sstevel@tonic-gate }
1171*7c478bd9Sstevel@tonic-gate 
1172*7c478bd9Sstevel@tonic-gate /*
1173*7c478bd9Sstevel@tonic-gate  * Return the "best" allowable delegation available given the current
1174*7c478bd9Sstevel@tonic-gate  * delegation type and the desired access and deny modes on the file.
1175*7c478bd9Sstevel@tonic-gate  * At the point that this routine is called we know that the access and
1176*7c478bd9Sstevel@tonic-gate  * deny modes are consistent with the file modes.
1177*7c478bd9Sstevel@tonic-gate  */
1178*7c478bd9Sstevel@tonic-gate static open_delegation_type4
1179*7c478bd9Sstevel@tonic-gate rfs4_check_delegation(rfs4_state_t *sp, rfs4_file_t *fp)
1180*7c478bd9Sstevel@tonic-gate {
1181*7c478bd9Sstevel@tonic-gate 	open_delegation_type4 dtype = fp->dinfo->dtype;
1182*7c478bd9Sstevel@tonic-gate 	uint32_t access = sp->share_access;
1183*7c478bd9Sstevel@tonic-gate 	uint32_t deny = sp->share_deny;
1184*7c478bd9Sstevel@tonic-gate 	int readcnt = 0;
1185*7c478bd9Sstevel@tonic-gate 	int writecnt = 0;
1186*7c478bd9Sstevel@tonic-gate 
1187*7c478bd9Sstevel@tonic-gate 	switch (dtype) {
1188*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_NONE:
1189*7c478bd9Sstevel@tonic-gate 		/*
1190*7c478bd9Sstevel@tonic-gate 		 * Determine if more than just this OPEN have the file
1191*7c478bd9Sstevel@tonic-gate 		 * open and if so, no delegation may be provided to
1192*7c478bd9Sstevel@tonic-gate 		 * the client.
1193*7c478bd9Sstevel@tonic-gate 		 */
1194*7c478bd9Sstevel@tonic-gate 		if (access & OPEN4_SHARE_ACCESS_WRITE)
1195*7c478bd9Sstevel@tonic-gate 			writecnt++;
1196*7c478bd9Sstevel@tonic-gate 		if (access & OPEN4_SHARE_ACCESS_READ)
1197*7c478bd9Sstevel@tonic-gate 			readcnt++;
1198*7c478bd9Sstevel@tonic-gate 
1199*7c478bd9Sstevel@tonic-gate 		if (fp->access_read > readcnt || fp->access_write > writecnt)
1200*7c478bd9Sstevel@tonic-gate 			return (OPEN_DELEGATE_NONE);
1201*7c478bd9Sstevel@tonic-gate 
1202*7c478bd9Sstevel@tonic-gate 		/*
1203*7c478bd9Sstevel@tonic-gate 		 * If the client is going to write, or if the client
1204*7c478bd9Sstevel@tonic-gate 		 * has exclusive access, return a write delegation.
1205*7c478bd9Sstevel@tonic-gate 		 */
1206*7c478bd9Sstevel@tonic-gate 		if ((access & OPEN4_SHARE_ACCESS_WRITE) ||
1207*7c478bd9Sstevel@tonic-gate 		    (deny & (OPEN4_SHARE_DENY_READ | OPEN4_SHARE_DENY_WRITE)))
1208*7c478bd9Sstevel@tonic-gate 			return (OPEN_DELEGATE_WRITE);
1209*7c478bd9Sstevel@tonic-gate 		/*
1210*7c478bd9Sstevel@tonic-gate 		 * If we don't want to write or we've haven't denied read
1211*7c478bd9Sstevel@tonic-gate 		 * access to others, return a read delegation.
1212*7c478bd9Sstevel@tonic-gate 		 */
1213*7c478bd9Sstevel@tonic-gate 		if ((access & ~OPEN4_SHARE_ACCESS_WRITE) ||
1214*7c478bd9Sstevel@tonic-gate 			(deny & ~OPEN4_SHARE_DENY_READ))
1215*7c478bd9Sstevel@tonic-gate 			return (OPEN_DELEGATE_READ);
1216*7c478bd9Sstevel@tonic-gate 
1217*7c478bd9Sstevel@tonic-gate 		/* Shouldn't get here */
1218*7c478bd9Sstevel@tonic-gate 		return (OPEN_DELEGATE_NONE);
1219*7c478bd9Sstevel@tonic-gate 
1220*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_READ:
1221*7c478bd9Sstevel@tonic-gate 		/*
1222*7c478bd9Sstevel@tonic-gate 		 * If the file is delegated for read but we wan't to
1223*7c478bd9Sstevel@tonic-gate 		 * write or deny others to read then we can't delegate
1224*7c478bd9Sstevel@tonic-gate 		 * the file. We shouldn't get here since the delegation should
1225*7c478bd9Sstevel@tonic-gate 		 * have been recalled already.
1226*7c478bd9Sstevel@tonic-gate 		 */
1227*7c478bd9Sstevel@tonic-gate 		if ((access & OPEN4_SHARE_ACCESS_WRITE) ||
1228*7c478bd9Sstevel@tonic-gate 		    (deny & OPEN4_SHARE_DENY_READ))
1229*7c478bd9Sstevel@tonic-gate 		    return (OPEN_DELEGATE_NONE);
1230*7c478bd9Sstevel@tonic-gate 		return (OPEN_DELEGATE_READ);
1231*7c478bd9Sstevel@tonic-gate 
1232*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_WRITE:
1233*7c478bd9Sstevel@tonic-gate 		return (OPEN_DELEGATE_WRITE);
1234*7c478bd9Sstevel@tonic-gate 	}
1235*7c478bd9Sstevel@tonic-gate 
1236*7c478bd9Sstevel@tonic-gate 	/* Shouldn't get here */
1237*7c478bd9Sstevel@tonic-gate 	return (OPEN_DELEGATE_NONE);
1238*7c478bd9Sstevel@tonic-gate }
1239*7c478bd9Sstevel@tonic-gate 
1240*7c478bd9Sstevel@tonic-gate /*
1241*7c478bd9Sstevel@tonic-gate  * Given the desired delegation type and the "history" of the file
1242*7c478bd9Sstevel@tonic-gate  * determine the actual delegation type to return.
1243*7c478bd9Sstevel@tonic-gate  */
1244*7c478bd9Sstevel@tonic-gate static open_delegation_type4
1245*7c478bd9Sstevel@tonic-gate rfs4_delegation_policy(open_delegation_type4 dtype,
1246*7c478bd9Sstevel@tonic-gate 	rfs4_dinfo_t *dinfo, clientid4 cid)
1247*7c478bd9Sstevel@tonic-gate {
1248*7c478bd9Sstevel@tonic-gate 	time_t elapsed;
1249*7c478bd9Sstevel@tonic-gate 
1250*7c478bd9Sstevel@tonic-gate 	if (rfs4_deleg_policy != SRV_NORMAL_DELEGATE)
1251*7c478bd9Sstevel@tonic-gate 		return (OPEN_DELEGATE_NONE);
1252*7c478bd9Sstevel@tonic-gate 
1253*7c478bd9Sstevel@tonic-gate 	/*
1254*7c478bd9Sstevel@tonic-gate 	 * Has this file/delegation ever been recalled?  If not then
1255*7c478bd9Sstevel@tonic-gate 	 * no furhter checks for a delegation race need to be done.
1256*7c478bd9Sstevel@tonic-gate 	 * However if a recall has occurred, then check to see if a
1257*7c478bd9Sstevel@tonic-gate 	 * client has caused its own delegation recall to occur.  If
1258*7c478bd9Sstevel@tonic-gate 	 * not, then has a delegation for this file been returned
1259*7c478bd9Sstevel@tonic-gate 	 * recently?  If so, then do not assign a new delegation to
1260*7c478bd9Sstevel@tonic-gate 	 * avoid a "delegation race" between the original client and
1261*7c478bd9Sstevel@tonic-gate 	 * the new/conflicting client.
1262*7c478bd9Sstevel@tonic-gate 	 */
1263*7c478bd9Sstevel@tonic-gate 	if (dinfo->ever_recalled == TRUE) {
1264*7c478bd9Sstevel@tonic-gate 		if (dinfo->conflicted_client != cid) {
1265*7c478bd9Sstevel@tonic-gate 			elapsed = gethrestime_sec() - dinfo->time_returned;
1266*7c478bd9Sstevel@tonic-gate 			if (elapsed < rfs4_lease_time)
1267*7c478bd9Sstevel@tonic-gate 				return (OPEN_DELEGATE_NONE);
1268*7c478bd9Sstevel@tonic-gate 		}
1269*7c478bd9Sstevel@tonic-gate 	}
1270*7c478bd9Sstevel@tonic-gate 
1271*7c478bd9Sstevel@tonic-gate 	/* Limit the number of read grants */
1272*7c478bd9Sstevel@tonic-gate 	if (dtype == OPEN_DELEGATE_READ &&
1273*7c478bd9Sstevel@tonic-gate 		dinfo->rdgrants > MAX_READ_DELEGATIONS)
1274*7c478bd9Sstevel@tonic-gate 		return (OPEN_DELEGATE_NONE);
1275*7c478bd9Sstevel@tonic-gate 
1276*7c478bd9Sstevel@tonic-gate 	/*
1277*7c478bd9Sstevel@tonic-gate 	 * Should consider limiting total number of read/write
1278*7c478bd9Sstevel@tonic-gate 	 * delegations the server will permit.
1279*7c478bd9Sstevel@tonic-gate 	 */
1280*7c478bd9Sstevel@tonic-gate 
1281*7c478bd9Sstevel@tonic-gate 	return (dtype);
1282*7c478bd9Sstevel@tonic-gate }
1283*7c478bd9Sstevel@tonic-gate 
1284*7c478bd9Sstevel@tonic-gate /*
1285*7c478bd9Sstevel@tonic-gate  * Try and grant a delegation for an open give the state. The routine
1286*7c478bd9Sstevel@tonic-gate  * returns the delegation type granted. This could be OPEN_DELEGATE_NONE.
1287*7c478bd9Sstevel@tonic-gate  *
1288*7c478bd9Sstevel@tonic-gate  * The state and associate file entry must be locked
1289*7c478bd9Sstevel@tonic-gate  */
1290*7c478bd9Sstevel@tonic-gate rfs4_deleg_state_t *
1291*7c478bd9Sstevel@tonic-gate rfs4_grant_delegation(delegreq_t dreq, rfs4_state_t *sp, int *recall)
1292*7c478bd9Sstevel@tonic-gate {
1293*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = sp->finfo;
1294*7c478bd9Sstevel@tonic-gate 	open_delegation_type4 dtype;
1295*7c478bd9Sstevel@tonic-gate 	int no_delegation;
1296*7c478bd9Sstevel@tonic-gate 
1297*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_dbe_islocked(sp->dbe));
1298*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_dbe_islocked(fp->dbe));
1299*7c478bd9Sstevel@tonic-gate 
1300*7c478bd9Sstevel@tonic-gate 	/* Is the server even providing delegations? */
1301*7c478bd9Sstevel@tonic-gate 	if (rfs4_deleg_policy == SRV_NEVER_DELEGATE || dreq == DELEG_NONE)
1302*7c478bd9Sstevel@tonic-gate 		return (NULL);
1303*7c478bd9Sstevel@tonic-gate 
1304*7c478bd9Sstevel@tonic-gate 	/* Check to see if delegations have been temporarily disabled */
1305*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rfs4_deleg_lock);
1306*7c478bd9Sstevel@tonic-gate 	no_delegation = rfs4_deleg_disabled;
1307*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rfs4_deleg_lock);
1308*7c478bd9Sstevel@tonic-gate 
1309*7c478bd9Sstevel@tonic-gate 	if (no_delegation)
1310*7c478bd9Sstevel@tonic-gate 		return (NULL);
1311*7c478bd9Sstevel@tonic-gate 
1312*7c478bd9Sstevel@tonic-gate 	/* Don't grant a delegation if a deletion is impending. */
1313*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->hold_grant > 0) {
1314*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_deleg_debug,
1315*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "rfs4_grant_delegation: hold_grant is set"));
1316*7c478bd9Sstevel@tonic-gate 		return (NULL);
1317*7c478bd9Sstevel@tonic-gate 	}
1318*7c478bd9Sstevel@tonic-gate 
1319*7c478bd9Sstevel@tonic-gate 	/*
1320*7c478bd9Sstevel@tonic-gate 	 * Don't grant a delegation if there are any lock manager
1321*7c478bd9Sstevel@tonic-gate 	 * (NFSv2/v3) locks for the file.  This is a bit of a hack (e.g.,
1322*7c478bd9Sstevel@tonic-gate 	 * if there are only read locks we should be able to grant a
1323*7c478bd9Sstevel@tonic-gate 	 * read-only delegation), but it's good enough for now.
1324*7c478bd9Sstevel@tonic-gate 	 *
1325*7c478bd9Sstevel@tonic-gate 	 * MT safety: the lock manager checks for conflicting delegations
1326*7c478bd9Sstevel@tonic-gate 	 * before processing a lock request.  That check will block until
1327*7c478bd9Sstevel@tonic-gate 	 * we are done here.  So if the lock manager acquires a lock after
1328*7c478bd9Sstevel@tonic-gate 	 * we decide to grant the delegation, the delegation will get
1329*7c478bd9Sstevel@tonic-gate 	 * immediately recalled (if there's a conflict), so we're safe.
1330*7c478bd9Sstevel@tonic-gate 	 */
1331*7c478bd9Sstevel@tonic-gate 	if (lm_vp_active(fp->vp)) {
1332*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_deleg_debug,
1333*7c478bd9Sstevel@tonic-gate 		    (CE_NOTE, "rfs4_grant_delegation: NLM lock"));
1334*7c478bd9Sstevel@tonic-gate 		return (NULL);
1335*7c478bd9Sstevel@tonic-gate 	}
1336*7c478bd9Sstevel@tonic-gate 
1337*7c478bd9Sstevel@tonic-gate 	/*
1338*7c478bd9Sstevel@tonic-gate 	 * Based on the type of delegation request passed in, take the
1339*7c478bd9Sstevel@tonic-gate 	 * appropriate action (DELEG_NONE is handled above)
1340*7c478bd9Sstevel@tonic-gate 	 */
1341*7c478bd9Sstevel@tonic-gate 	switch (dreq) {
1342*7c478bd9Sstevel@tonic-gate 
1343*7c478bd9Sstevel@tonic-gate 	case DELEG_READ:
1344*7c478bd9Sstevel@tonic-gate 	case DELEG_WRITE:
1345*7c478bd9Sstevel@tonic-gate 		/*
1346*7c478bd9Sstevel@tonic-gate 		 * The server "must" grant the delegation in this case.
1347*7c478bd9Sstevel@tonic-gate 		 * Client is using open previous
1348*7c478bd9Sstevel@tonic-gate 		 */
1349*7c478bd9Sstevel@tonic-gate 		dtype = (open_delegation_type4)dreq;
1350*7c478bd9Sstevel@tonic-gate 		*recall = 1;
1351*7c478bd9Sstevel@tonic-gate 		break;
1352*7c478bd9Sstevel@tonic-gate 	case DELEG_ANY:
1353*7c478bd9Sstevel@tonic-gate 		/*
1354*7c478bd9Sstevel@tonic-gate 		 * If a valid callback path does not exist, no delegation may
1355*7c478bd9Sstevel@tonic-gate 		 * be granted.
1356*7c478bd9Sstevel@tonic-gate 		 */
1357*7c478bd9Sstevel@tonic-gate 		if (sp->owner->client->cbinfo.cb_state != CB_OK)
1358*7c478bd9Sstevel@tonic-gate 			return (NULL);
1359*7c478bd9Sstevel@tonic-gate 
1360*7c478bd9Sstevel@tonic-gate 		/*
1361*7c478bd9Sstevel@tonic-gate 		 * If the original operation which caused time_rm_delayed
1362*7c478bd9Sstevel@tonic-gate 		 * to be set hasn't been retried and completed for one
1363*7c478bd9Sstevel@tonic-gate 		 * full lease period, clear it and allow delegations to
1364*7c478bd9Sstevel@tonic-gate 		 * get granted again.
1365*7c478bd9Sstevel@tonic-gate 		 */
1366*7c478bd9Sstevel@tonic-gate 		if (fp->dinfo->time_rm_delayed > 0 &&
1367*7c478bd9Sstevel@tonic-gate 		    gethrestime_sec() >
1368*7c478bd9Sstevel@tonic-gate 		    fp->dinfo->time_rm_delayed + rfs4_lease_time)
1369*7c478bd9Sstevel@tonic-gate 			fp->dinfo->time_rm_delayed = 0;
1370*7c478bd9Sstevel@tonic-gate 
1371*7c478bd9Sstevel@tonic-gate 		/*
1372*7c478bd9Sstevel@tonic-gate 		 * If we are waiting for a delegation to be returned then
1373*7c478bd9Sstevel@tonic-gate 		 * don't delegate this file. We do this for correctness as
1374*7c478bd9Sstevel@tonic-gate 		 * well as if the file is being recalled we would likely
1375*7c478bd9Sstevel@tonic-gate 		 * recall this file again.
1376*7c478bd9Sstevel@tonic-gate 		 */
1377*7c478bd9Sstevel@tonic-gate 
1378*7c478bd9Sstevel@tonic-gate 		if (fp->dinfo->time_recalled != 0 ||
1379*7c478bd9Sstevel@tonic-gate 		    fp->dinfo->time_rm_delayed != 0)
1380*7c478bd9Sstevel@tonic-gate 			return (NULL);
1381*7c478bd9Sstevel@tonic-gate 
1382*7c478bd9Sstevel@tonic-gate 		/* Get the "best" delegation candidate */
1383*7c478bd9Sstevel@tonic-gate 		dtype = rfs4_check_delegation(sp, fp);
1384*7c478bd9Sstevel@tonic-gate 
1385*7c478bd9Sstevel@tonic-gate 		if (dtype == OPEN_DELEGATE_NONE)
1386*7c478bd9Sstevel@tonic-gate 			return (NULL);
1387*7c478bd9Sstevel@tonic-gate 
1388*7c478bd9Sstevel@tonic-gate 		/*
1389*7c478bd9Sstevel@tonic-gate 		 * Based on policy and the history of the file get the
1390*7c478bd9Sstevel@tonic-gate 		 * actual delegation.
1391*7c478bd9Sstevel@tonic-gate 		 */
1392*7c478bd9Sstevel@tonic-gate 		dtype = rfs4_delegation_policy(dtype, fp->dinfo,
1393*7c478bd9Sstevel@tonic-gate 			sp->owner->client->clientid);
1394*7c478bd9Sstevel@tonic-gate 
1395*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_deleg_debug,
1396*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "Grant policy dtype = %d", dtype));
1397*7c478bd9Sstevel@tonic-gate 		if (dtype == OPEN_DELEGATE_NONE)
1398*7c478bd9Sstevel@tonic-gate 			return (NULL);
1399*7c478bd9Sstevel@tonic-gate 		break;
1400*7c478bd9Sstevel@tonic-gate 	default:
1401*7c478bd9Sstevel@tonic-gate 		return (NULL);
1402*7c478bd9Sstevel@tonic-gate 	}
1403*7c478bd9Sstevel@tonic-gate 
1404*7c478bd9Sstevel@tonic-gate 	/* set the delegation for the state */
1405*7c478bd9Sstevel@tonic-gate 	return (rfs4_deleg_state(sp, dtype, recall));
1406*7c478bd9Sstevel@tonic-gate }
1407*7c478bd9Sstevel@tonic-gate 
1408*7c478bd9Sstevel@tonic-gate void
1409*7c478bd9Sstevel@tonic-gate rfs4_set_deleg_response(rfs4_deleg_state_t *dsp, open_delegation4 *dp,
1410*7c478bd9Sstevel@tonic-gate 			nfsace4 *ace,  int recall)
1411*7c478bd9Sstevel@tonic-gate {
1412*7c478bd9Sstevel@tonic-gate 	open_write_delegation4 *wp;
1413*7c478bd9Sstevel@tonic-gate 	open_read_delegation4 *rp;
1414*7c478bd9Sstevel@tonic-gate 	nfs_space_limit4 *spl;
1415*7c478bd9Sstevel@tonic-gate 	nfsace4 nace;
1416*7c478bd9Sstevel@tonic-gate 
1417*7c478bd9Sstevel@tonic-gate 	/*
1418*7c478bd9Sstevel@tonic-gate 	 * We need to allocate a new copy of the who string.
1419*7c478bd9Sstevel@tonic-gate 	 * this string will be freed by the rfs4_op_open dis_resfree
1420*7c478bd9Sstevel@tonic-gate 	 * routine. We need to do this allocation since replays will
1421*7c478bd9Sstevel@tonic-gate 	 * be allocated and rfs4_compound can't tell the difference from
1422*7c478bd9Sstevel@tonic-gate 	 * a replay and an inital open. N.B. if an ace is passed in, it
1423*7c478bd9Sstevel@tonic-gate 	 * the caller's responsibility to free it.
1424*7c478bd9Sstevel@tonic-gate 	 */
1425*7c478bd9Sstevel@tonic-gate 
1426*7c478bd9Sstevel@tonic-gate 	if (ace == NULL) {
1427*7c478bd9Sstevel@tonic-gate 		/*
1428*7c478bd9Sstevel@tonic-gate 		 * Default is to deny all access, the client will have
1429*7c478bd9Sstevel@tonic-gate 		 * to contact the server.  XXX Do we want to actually
1430*7c478bd9Sstevel@tonic-gate 		 * set a deny for every one, or do we simply want to
1431*7c478bd9Sstevel@tonic-gate 		 * construct an entity that will match no one?
1432*7c478bd9Sstevel@tonic-gate 		 */
1433*7c478bd9Sstevel@tonic-gate 		nace.type = ACE4_ACCESS_DENIED_ACE_TYPE;
1434*7c478bd9Sstevel@tonic-gate 		nace.flag = 0;
1435*7c478bd9Sstevel@tonic-gate 		nace.access_mask = ACE4_VALID_MASK_BITS;
1436*7c478bd9Sstevel@tonic-gate 		(void) str_to_utf8(ACE4_WHO_EVERYONE, &nace.who);
1437*7c478bd9Sstevel@tonic-gate 	} else {
1438*7c478bd9Sstevel@tonic-gate 		nace.type = ace->type;
1439*7c478bd9Sstevel@tonic-gate 		nace.flag = ace->flag;
1440*7c478bd9Sstevel@tonic-gate 		nace.access_mask = ace->access_mask;
1441*7c478bd9Sstevel@tonic-gate 		(void) utf8_copy(&ace->who, &nace.who);
1442*7c478bd9Sstevel@tonic-gate 	}
1443*7c478bd9Sstevel@tonic-gate 
1444*7c478bd9Sstevel@tonic-gate 	dp->delegation_type = dsp->dtype;
1445*7c478bd9Sstevel@tonic-gate 
1446*7c478bd9Sstevel@tonic-gate 	switch (dsp->dtype) {
1447*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_NONE:
1448*7c478bd9Sstevel@tonic-gate 		break;
1449*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_READ:
1450*7c478bd9Sstevel@tonic-gate 		rp = &dp->open_delegation4_u.read;
1451*7c478bd9Sstevel@tonic-gate 		rp->stateid = dsp->delegid.stateid;
1452*7c478bd9Sstevel@tonic-gate 		rp->recall = (bool_t)recall;
1453*7c478bd9Sstevel@tonic-gate 		rp->permissions = nace;
1454*7c478bd9Sstevel@tonic-gate 		break;
1455*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_WRITE:
1456*7c478bd9Sstevel@tonic-gate 		wp = &dp->open_delegation4_u.write;
1457*7c478bd9Sstevel@tonic-gate 		wp->stateid = dsp->delegid.stateid;
1458*7c478bd9Sstevel@tonic-gate 		wp->recall = (bool_t)recall;
1459*7c478bd9Sstevel@tonic-gate 		spl = &wp->space_limit;
1460*7c478bd9Sstevel@tonic-gate 		spl->limitby = NFS_LIMIT_SIZE;
1461*7c478bd9Sstevel@tonic-gate 		spl->nfs_space_limit4_u.filesize = 0;
1462*7c478bd9Sstevel@tonic-gate 		wp->permissions = nace;
1463*7c478bd9Sstevel@tonic-gate 		break;
1464*7c478bd9Sstevel@tonic-gate 	}
1465*7c478bd9Sstevel@tonic-gate }
1466*7c478bd9Sstevel@tonic-gate 
1467*7c478bd9Sstevel@tonic-gate /*
1468*7c478bd9Sstevel@tonic-gate  * Check if the file is delegated via the provided file struct.
1469*7c478bd9Sstevel@tonic-gate  * Return TRUE if it is delegated.  This is intended for use by
1470*7c478bd9Sstevel@tonic-gate  * the v4 server.  The v2/v3 server code should use rfs4_check_delegated().
1471*7c478bd9Sstevel@tonic-gate  *
1472*7c478bd9Sstevel@tonic-gate  * Note that if the file is found to have a delegation, it is
1473*7c478bd9Sstevel@tonic-gate  * recalled, unless the clientid of the caller matches the clientid of the
1474*7c478bd9Sstevel@tonic-gate  * delegation. If the caller has specified, there is a slight delay
1475*7c478bd9Sstevel@tonic-gate  * inserted in the hopes that the delegation will be returned quickly.
1476*7c478bd9Sstevel@tonic-gate  */
1477*7c478bd9Sstevel@tonic-gate bool_t
1478*7c478bd9Sstevel@tonic-gate rfs4_check_delegated_byfp(int mode, rfs4_file_t *fp,
1479*7c478bd9Sstevel@tonic-gate 	bool_t trunc, bool_t do_delay, bool_t is_rm, clientid4 *cp)
1480*7c478bd9Sstevel@tonic-gate {
1481*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
1482*7c478bd9Sstevel@tonic-gate 
1483*7c478bd9Sstevel@tonic-gate 	/* Is delegation enabled? */
1484*7c478bd9Sstevel@tonic-gate 	if (rfs4_deleg_policy == SRV_NEVER_DELEGATE)
1485*7c478bd9Sstevel@tonic-gate 		return (FALSE);
1486*7c478bd9Sstevel@tonic-gate 
1487*7c478bd9Sstevel@tonic-gate 	/* do we have a delegation on this file? */
1488*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(fp->dbe);
1489*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->dtype == OPEN_DELEGATE_NONE) {
1490*7c478bd9Sstevel@tonic-gate 		if (is_rm)
1491*7c478bd9Sstevel@tonic-gate 			fp->dinfo->hold_grant++;
1492*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(fp->dbe);
1493*7c478bd9Sstevel@tonic-gate 		return (FALSE);
1494*7c478bd9Sstevel@tonic-gate 	}
1495*7c478bd9Sstevel@tonic-gate 	/*
1496*7c478bd9Sstevel@tonic-gate 	 * do we have a write delegation on this file or are we
1497*7c478bd9Sstevel@tonic-gate 	 * requesting write access to a file with any type of existing
1498*7c478bd9Sstevel@tonic-gate 	 * delegation?
1499*7c478bd9Sstevel@tonic-gate 	 */
1500*7c478bd9Sstevel@tonic-gate 	if (mode == FWRITE || fp->dinfo->dtype == OPEN_DELEGATE_WRITE) {
1501*7c478bd9Sstevel@tonic-gate 		if (cp != NULL) {
1502*7c478bd9Sstevel@tonic-gate 			dsp = fp->delegationlist.next->dsp;
1503*7c478bd9Sstevel@tonic-gate 			if (dsp == NULL) {
1504*7c478bd9Sstevel@tonic-gate 				rfs4_dbe_unlock(fp->dbe);
1505*7c478bd9Sstevel@tonic-gate 				return (FALSE);
1506*7c478bd9Sstevel@tonic-gate 			}
1507*7c478bd9Sstevel@tonic-gate 			/*
1508*7c478bd9Sstevel@tonic-gate 			 * Does the requestor already own the delegation?
1509*7c478bd9Sstevel@tonic-gate 			 */
1510*7c478bd9Sstevel@tonic-gate 			if (dsp->client->clientid == *(cp)) {
1511*7c478bd9Sstevel@tonic-gate 				rfs4_dbe_unlock(fp->dbe);
1512*7c478bd9Sstevel@tonic-gate 				return (FALSE);
1513*7c478bd9Sstevel@tonic-gate 			}
1514*7c478bd9Sstevel@tonic-gate 		}
1515*7c478bd9Sstevel@tonic-gate 
1516*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(fp->dbe);
1517*7c478bd9Sstevel@tonic-gate 		rfs4_recall_deleg(fp, trunc, NULL);
1518*7c478bd9Sstevel@tonic-gate 
1519*7c478bd9Sstevel@tonic-gate 		if (!do_delay) {
1520*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_lock(fp->dbe);
1521*7c478bd9Sstevel@tonic-gate 			fp->dinfo->time_rm_delayed = gethrestime_sec();
1522*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_unlock(fp->dbe);
1523*7c478bd9Sstevel@tonic-gate 			return (TRUE);
1524*7c478bd9Sstevel@tonic-gate 		}
1525*7c478bd9Sstevel@tonic-gate 
1526*7c478bd9Sstevel@tonic-gate 		delay(NFS4_DELEGATION_CONFLICT_DELAY);
1527*7c478bd9Sstevel@tonic-gate 
1528*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(fp->dbe);
1529*7c478bd9Sstevel@tonic-gate 		if (fp->dinfo->dtype != OPEN_DELEGATE_NONE) {
1530*7c478bd9Sstevel@tonic-gate 			fp->dinfo->time_rm_delayed = gethrestime_sec();
1531*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_unlock(fp->dbe);
1532*7c478bd9Sstevel@tonic-gate 			return (TRUE);
1533*7c478bd9Sstevel@tonic-gate 		}
1534*7c478bd9Sstevel@tonic-gate 	}
1535*7c478bd9Sstevel@tonic-gate 	if (is_rm)
1536*7c478bd9Sstevel@tonic-gate 		fp->dinfo->hold_grant++;
1537*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(fp->dbe);
1538*7c478bd9Sstevel@tonic-gate 	return (FALSE);
1539*7c478bd9Sstevel@tonic-gate }
1540*7c478bd9Sstevel@tonic-gate 
1541*7c478bd9Sstevel@tonic-gate /*
1542*7c478bd9Sstevel@tonic-gate  * Check if the file is delegated in the case of a v2 or v3 access.
1543*7c478bd9Sstevel@tonic-gate  * Return TRUE if it is delegated which in turn means that v2 should
1544*7c478bd9Sstevel@tonic-gate  * drop the request and in the case of v3 JUKEBOX should be returned.
1545*7c478bd9Sstevel@tonic-gate  */
1546*7c478bd9Sstevel@tonic-gate bool_t
1547*7c478bd9Sstevel@tonic-gate rfs4_check_delegated(int mode, vnode_t *vp, bool_t trunc)
1548*7c478bd9Sstevel@tonic-gate {
1549*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp;
1550*7c478bd9Sstevel@tonic-gate 	bool_t create = FALSE;
1551*7c478bd9Sstevel@tonic-gate 	bool_t rc = FALSE;
1552*7c478bd9Sstevel@tonic-gate 
1553*7c478bd9Sstevel@tonic-gate 	rfs4_hold_deleg_policy();
1554*7c478bd9Sstevel@tonic-gate 
1555*7c478bd9Sstevel@tonic-gate 	/* Is delegation enabled? */
1556*7c478bd9Sstevel@tonic-gate 	if (rfs4_deleg_policy != SRV_NEVER_DELEGATE) {
1557*7c478bd9Sstevel@tonic-gate 		fp = rfs4_findfile(vp, NULL, &create);
1558*7c478bd9Sstevel@tonic-gate 		if (fp != NULL) {
1559*7c478bd9Sstevel@tonic-gate 			if (rfs4_check_delegated_byfp(mode, fp, trunc,
1560*7c478bd9Sstevel@tonic-gate 						TRUE, FALSE, NULL)) {
1561*7c478bd9Sstevel@tonic-gate 				rc = TRUE;
1562*7c478bd9Sstevel@tonic-gate 			}
1563*7c478bd9Sstevel@tonic-gate 			rfs4_file_rele(fp);
1564*7c478bd9Sstevel@tonic-gate 		}
1565*7c478bd9Sstevel@tonic-gate 	}
1566*7c478bd9Sstevel@tonic-gate 	rfs4_rele_deleg_policy();
1567*7c478bd9Sstevel@tonic-gate 	return (rc);
1568*7c478bd9Sstevel@tonic-gate }
1569*7c478bd9Sstevel@tonic-gate 
1570*7c478bd9Sstevel@tonic-gate /*
1571*7c478bd9Sstevel@tonic-gate  * Release a hold on the hold_grant counter which
1572*7c478bd9Sstevel@tonic-gate  * prevents delegation from being granted while a remove
1573*7c478bd9Sstevel@tonic-gate  * or a rename is in progress.
1574*7c478bd9Sstevel@tonic-gate  */
1575*7c478bd9Sstevel@tonic-gate void
1576*7c478bd9Sstevel@tonic-gate rfs4_clear_dont_grant(rfs4_file_t *fp)
1577*7c478bd9Sstevel@tonic-gate {
1578*7c478bd9Sstevel@tonic-gate 	if (rfs4_deleg_policy == SRV_NEVER_DELEGATE)
1579*7c478bd9Sstevel@tonic-gate 		return;
1580*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(fp->dbe);
1581*7c478bd9Sstevel@tonic-gate 	ASSERT(fp->dinfo->hold_grant > 0);
1582*7c478bd9Sstevel@tonic-gate 	fp->dinfo->hold_grant--;
1583*7c478bd9Sstevel@tonic-gate 	fp->dinfo->time_rm_delayed = 0;
1584*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(fp->dbe);
1585*7c478bd9Sstevel@tonic-gate }
1586*7c478bd9Sstevel@tonic-gate 
1587*7c478bd9Sstevel@tonic-gate /*
1588*7c478bd9Sstevel@tonic-gate  * State support for delegation.
1589*7c478bd9Sstevel@tonic-gate  * Set the state delegation type for this state;
1590*7c478bd9Sstevel@tonic-gate  * This routine is called from open via rfs4_grant_delegation and the entry
1591*7c478bd9Sstevel@tonic-gate  * locks on sp and sp->finfo are assumed.
1592*7c478bd9Sstevel@tonic-gate  */
1593*7c478bd9Sstevel@tonic-gate static rfs4_deleg_state_t *
1594*7c478bd9Sstevel@tonic-gate rfs4_deleg_state(rfs4_state_t *sp, open_delegation_type4 dtype, int *recall)
1595*7c478bd9Sstevel@tonic-gate {
1596*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = sp->finfo;
1597*7c478bd9Sstevel@tonic-gate 	bool_t create = TRUE;
1598*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
1599*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
1600*7c478bd9Sstevel@tonic-gate 	int open_prev = *recall;
1601*7c478bd9Sstevel@tonic-gate 
1602*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_dbe_islocked(fp->dbe));
1603*7c478bd9Sstevel@tonic-gate 
1604*7c478bd9Sstevel@tonic-gate 	/* Shouldn't happen */
1605*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->recall_count != 0 ||
1606*7c478bd9Sstevel@tonic-gate 		(fp->dinfo->dtype == OPEN_DELEGATE_READ &&
1607*7c478bd9Sstevel@tonic-gate 			dtype != OPEN_DELEGATE_READ)) {
1608*7c478bd9Sstevel@tonic-gate 		return (NULL);
1609*7c478bd9Sstevel@tonic-gate 	}
1610*7c478bd9Sstevel@tonic-gate 
1611*7c478bd9Sstevel@tonic-gate 	/* Unlock to avoid deadlock */
1612*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(fp->dbe);
1613*7c478bd9Sstevel@tonic-gate 
1614*7c478bd9Sstevel@tonic-gate 	dsp = rfs4_finddeleg(sp, &create);
1615*7c478bd9Sstevel@tonic-gate 
1616*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(fp->dbe);
1617*7c478bd9Sstevel@tonic-gate 
1618*7c478bd9Sstevel@tonic-gate 	if (dsp == NULL)
1619*7c478bd9Sstevel@tonic-gate 		return (NULL);
1620*7c478bd9Sstevel@tonic-gate 
1621*7c478bd9Sstevel@tonic-gate 	if (create == FALSE) {
1622*7c478bd9Sstevel@tonic-gate 		if (sp->owner->client == dsp->client &&
1623*7c478bd9Sstevel@tonic-gate 		    dsp->dtype == dtype) {
1624*7c478bd9Sstevel@tonic-gate 			return (dsp);
1625*7c478bd9Sstevel@tonic-gate 		} else {
1626*7c478bd9Sstevel@tonic-gate 			rfs4_deleg_state_rele(dsp);
1627*7c478bd9Sstevel@tonic-gate 			return (NULL);
1628*7c478bd9Sstevel@tonic-gate 		}
1629*7c478bd9Sstevel@tonic-gate 	}
1630*7c478bd9Sstevel@tonic-gate 
1631*7c478bd9Sstevel@tonic-gate 	/*
1632*7c478bd9Sstevel@tonic-gate 	 * Check that this file has not been delegated to another
1633*7c478bd9Sstevel@tonic-gate 	 * client
1634*7c478bd9Sstevel@tonic-gate 	 */
1635*7c478bd9Sstevel@tonic-gate 	if (fp->dinfo->recall_count != 0 ||
1636*7c478bd9Sstevel@tonic-gate 		fp->dinfo->dtype == OPEN_DELEGATE_WRITE ||
1637*7c478bd9Sstevel@tonic-gate 		(fp->dinfo->dtype == OPEN_DELEGATE_READ &&
1638*7c478bd9Sstevel@tonic-gate 			dtype != OPEN_DELEGATE_READ)) {
1639*7c478bd9Sstevel@tonic-gate 		rfs4_deleg_state_rele(dsp);
1640*7c478bd9Sstevel@tonic-gate 		return (NULL);
1641*7c478bd9Sstevel@tonic-gate 	}
1642*7c478bd9Sstevel@tonic-gate 
1643*7c478bd9Sstevel@tonic-gate 	vp = fp->vp;
1644*7c478bd9Sstevel@tonic-gate 	/* vnevent_support returns 0 if file system supports vnevents */
1645*7c478bd9Sstevel@tonic-gate 	if (vnevent_support(vp)) {
1646*7c478bd9Sstevel@tonic-gate 		rfs4_deleg_state_rele(dsp);
1647*7c478bd9Sstevel@tonic-gate 		return (NULL);
1648*7c478bd9Sstevel@tonic-gate 	}
1649*7c478bd9Sstevel@tonic-gate 
1650*7c478bd9Sstevel@tonic-gate 	*recall = 0;
1651*7c478bd9Sstevel@tonic-gate 	if (dtype == OPEN_DELEGATE_READ) {
1652*7c478bd9Sstevel@tonic-gate 		if (vn_is_opened(vp, V_WRITE) || vn_is_mapped(vp, V_WRITE)) {
1653*7c478bd9Sstevel@tonic-gate 			if (open_prev) {
1654*7c478bd9Sstevel@tonic-gate 				*recall = 1;
1655*7c478bd9Sstevel@tonic-gate 			} else {
1656*7c478bd9Sstevel@tonic-gate 				rfs4_deleg_state_rele(dsp);
1657*7c478bd9Sstevel@tonic-gate 				return (NULL);
1658*7c478bd9Sstevel@tonic-gate 			}
1659*7c478bd9Sstevel@tonic-gate 		}
1660*7c478bd9Sstevel@tonic-gate 		(void) fem_install(vp, deleg_rdops, (void *)fp, OPUNIQ,
1661*7c478bd9Sstevel@tonic-gate 				rfs4_mon_hold, rfs4_mon_rele);
1662*7c478bd9Sstevel@tonic-gate 		if (vn_is_opened(vp, V_WRITE) || vn_is_mapped(vp, V_WRITE)) {
1663*7c478bd9Sstevel@tonic-gate 			if (open_prev) {
1664*7c478bd9Sstevel@tonic-gate 				*recall = 1;
1665*7c478bd9Sstevel@tonic-gate 			} else {
1666*7c478bd9Sstevel@tonic-gate 				(void) fem_uninstall(vp, deleg_rdops,
1667*7c478bd9Sstevel@tonic-gate 						    (void *)fp);
1668*7c478bd9Sstevel@tonic-gate 				rfs4_deleg_state_rele(dsp);
1669*7c478bd9Sstevel@tonic-gate 				return (NULL);
1670*7c478bd9Sstevel@tonic-gate 			}
1671*7c478bd9Sstevel@tonic-gate 		}
1672*7c478bd9Sstevel@tonic-gate 	} else { /* WRITE */
1673*7c478bd9Sstevel@tonic-gate 		if (vn_is_opened(vp, V_RDORWR) || vn_is_mapped(vp, V_RDORWR)) {
1674*7c478bd9Sstevel@tonic-gate 			if (open_prev) {
1675*7c478bd9Sstevel@tonic-gate 				*recall = 1;
1676*7c478bd9Sstevel@tonic-gate 			} else {
1677*7c478bd9Sstevel@tonic-gate 				rfs4_deleg_state_rele(dsp);
1678*7c478bd9Sstevel@tonic-gate 				return (NULL);
1679*7c478bd9Sstevel@tonic-gate 			}
1680*7c478bd9Sstevel@tonic-gate 		}
1681*7c478bd9Sstevel@tonic-gate 		(void) fem_install(vp, deleg_wrops, (void *)fp, OPUNIQ,
1682*7c478bd9Sstevel@tonic-gate 				rfs4_mon_hold, rfs4_mon_rele);
1683*7c478bd9Sstevel@tonic-gate 		if (vn_is_opened(vp, V_RDORWR) || vn_is_mapped(vp, V_RDORWR)) {
1684*7c478bd9Sstevel@tonic-gate 			if (open_prev) {
1685*7c478bd9Sstevel@tonic-gate 				*recall = 1;
1686*7c478bd9Sstevel@tonic-gate 			} else {
1687*7c478bd9Sstevel@tonic-gate 				(void) fem_uninstall(vp, deleg_wrops,
1688*7c478bd9Sstevel@tonic-gate 							(void *)fp);
1689*7c478bd9Sstevel@tonic-gate 				rfs4_deleg_state_rele(dsp);
1690*7c478bd9Sstevel@tonic-gate 				return (NULL);
1691*7c478bd9Sstevel@tonic-gate 			}
1692*7c478bd9Sstevel@tonic-gate 		}
1693*7c478bd9Sstevel@tonic-gate 	}
1694*7c478bd9Sstevel@tonic-gate 	/* Place on delegation list for file */
1695*7c478bd9Sstevel@tonic-gate 	insque(&dsp->delegationlist, fp->delegationlist.prev);
1696*7c478bd9Sstevel@tonic-gate 
1697*7c478bd9Sstevel@tonic-gate 	dsp->dtype = fp->dinfo->dtype = dtype;
1698*7c478bd9Sstevel@tonic-gate 
1699*7c478bd9Sstevel@tonic-gate 	/* Update delegation stats for this file */
1700*7c478bd9Sstevel@tonic-gate 	fp->dinfo->time_lastgrant = gethrestime_sec();
1701*7c478bd9Sstevel@tonic-gate 
1702*7c478bd9Sstevel@tonic-gate 	/* reset since this is a new delegation */
1703*7c478bd9Sstevel@tonic-gate 	fp->dinfo->conflicted_client = 0;
1704*7c478bd9Sstevel@tonic-gate 	fp->dinfo->ever_recalled = FALSE;
1705*7c478bd9Sstevel@tonic-gate 
1706*7c478bd9Sstevel@tonic-gate 	if (dtype == OPEN_DELEGATE_READ)
1707*7c478bd9Sstevel@tonic-gate 		fp->dinfo->rdgrants++;
1708*7c478bd9Sstevel@tonic-gate 	else
1709*7c478bd9Sstevel@tonic-gate 		fp->dinfo->wrgrants++;
1710*7c478bd9Sstevel@tonic-gate 
1711*7c478bd9Sstevel@tonic-gate 	return (dsp);
1712*7c478bd9Sstevel@tonic-gate }
1713*7c478bd9Sstevel@tonic-gate 
1714*7c478bd9Sstevel@tonic-gate /*
1715*7c478bd9Sstevel@tonic-gate  * State routine for the server when a delegation is returned.
1716*7c478bd9Sstevel@tonic-gate  */
1717*7c478bd9Sstevel@tonic-gate void
1718*7c478bd9Sstevel@tonic-gate rfs4_return_deleg(rfs4_deleg_state_t *dsp, bool_t revoked)
1719*7c478bd9Sstevel@tonic-gate {
1720*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = dsp->finfo;
1721*7c478bd9Sstevel@tonic-gate 	open_delegation_type4 dtypewas;
1722*7c478bd9Sstevel@tonic-gate 
1723*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(fp->dbe);
1724*7c478bd9Sstevel@tonic-gate 	/* Remove state from recall list */
1725*7c478bd9Sstevel@tonic-gate 
1726*7c478bd9Sstevel@tonic-gate 	remque(&dsp->delegationlist);
1727*7c478bd9Sstevel@tonic-gate 	dsp->delegationlist.next = dsp->delegationlist.prev =
1728*7c478bd9Sstevel@tonic-gate 		&dsp->delegationlist;
1729*7c478bd9Sstevel@tonic-gate 
1730*7c478bd9Sstevel@tonic-gate 	if (&fp->delegationlist == fp->delegationlist.next) {
1731*7c478bd9Sstevel@tonic-gate 		dtypewas = fp->dinfo->dtype;
1732*7c478bd9Sstevel@tonic-gate 		fp->dinfo->dtype = OPEN_DELEGATE_NONE;
1733*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_cv_broadcast(fp->dbe);
1734*7c478bd9Sstevel@tonic-gate 
1735*7c478bd9Sstevel@tonic-gate 		/* if file system was unshared, the vp will be NULL */
1736*7c478bd9Sstevel@tonic-gate 		if (fp->vp != NULL) {
1737*7c478bd9Sstevel@tonic-gate 			if (dtypewas == OPEN_DELEGATE_READ)
1738*7c478bd9Sstevel@tonic-gate 				(void) fem_uninstall(fp->vp, deleg_rdops,
1739*7c478bd9Sstevel@tonic-gate 						(void *)fp);
1740*7c478bd9Sstevel@tonic-gate 			else
1741*7c478bd9Sstevel@tonic-gate 				(void) fem_uninstall(fp->vp, deleg_wrops,
1742*7c478bd9Sstevel@tonic-gate 						(void *)fp);
1743*7c478bd9Sstevel@tonic-gate 		}
1744*7c478bd9Sstevel@tonic-gate 	}
1745*7c478bd9Sstevel@tonic-gate 
1746*7c478bd9Sstevel@tonic-gate 	switch (dsp->dtype) {
1747*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_READ:
1748*7c478bd9Sstevel@tonic-gate 		fp->dinfo->rdgrants--;
1749*7c478bd9Sstevel@tonic-gate 		break;
1750*7c478bd9Sstevel@tonic-gate 	case OPEN_DELEGATE_WRITE:
1751*7c478bd9Sstevel@tonic-gate 		fp->dinfo->wrgrants--;
1752*7c478bd9Sstevel@tonic-gate 		break;
1753*7c478bd9Sstevel@tonic-gate 	default:
1754*7c478bd9Sstevel@tonic-gate 		break;
1755*7c478bd9Sstevel@tonic-gate 	}
1756*7c478bd9Sstevel@tonic-gate 
1757*7c478bd9Sstevel@tonic-gate 	/* used in the policy decision */
1758*7c478bd9Sstevel@tonic-gate 	fp->dinfo->time_returned = gethrestime_sec();
1759*7c478bd9Sstevel@tonic-gate 
1760*7c478bd9Sstevel@tonic-gate 	/*
1761*7c478bd9Sstevel@tonic-gate 	 * reset the time_recalled field so future delegations are not
1762*7c478bd9Sstevel@tonic-gate 	 * accidentally revoked
1763*7c478bd9Sstevel@tonic-gate 	 */
1764*7c478bd9Sstevel@tonic-gate 	if ((fp->dinfo->rdgrants + fp->dinfo->wrgrants) == 0)
1765*7c478bd9Sstevel@tonic-gate 		fp->dinfo->time_recalled = 0;
1766*7c478bd9Sstevel@tonic-gate 
1767*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(fp->dbe);
1768*7c478bd9Sstevel@tonic-gate 
1769*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(dsp->dbe);
1770*7c478bd9Sstevel@tonic-gate 
1771*7c478bd9Sstevel@tonic-gate 	dsp->dtype = OPEN_DELEGATE_NONE;
1772*7c478bd9Sstevel@tonic-gate 
1773*7c478bd9Sstevel@tonic-gate 	if (revoked == TRUE)
1774*7c478bd9Sstevel@tonic-gate 		dsp->time_revoked = gethrestime_sec();
1775*7c478bd9Sstevel@tonic-gate 
1776*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_invalidate(dsp->dbe);
1777*7c478bd9Sstevel@tonic-gate 
1778*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(dsp->dbe);
1779*7c478bd9Sstevel@tonic-gate 
1780*7c478bd9Sstevel@tonic-gate 	if (revoked == TRUE) {
1781*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(dsp->client->dbe);
1782*7c478bd9Sstevel@tonic-gate 		dsp->client->deleg_revoked++;	/* observability */
1783*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(dsp->client->dbe);
1784*7c478bd9Sstevel@tonic-gate 	}
1785*7c478bd9Sstevel@tonic-gate }
1786*7c478bd9Sstevel@tonic-gate 
1787*7c478bd9Sstevel@tonic-gate static void
1788*7c478bd9Sstevel@tonic-gate rfs4_revoke_deleg(rfs4_deleg_state_t *dsp)
1789*7c478bd9Sstevel@tonic-gate {
1790*7c478bd9Sstevel@tonic-gate 	rfs4_return_deleg(dsp, TRUE);
1791*7c478bd9Sstevel@tonic-gate }
1792*7c478bd9Sstevel@tonic-gate 
1793*7c478bd9Sstevel@tonic-gate static void
1794*7c478bd9Sstevel@tonic-gate rfs4_revoke_file(rfs4_file_t *fp)
1795*7c478bd9Sstevel@tonic-gate {
1796*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
1797*7c478bd9Sstevel@tonic-gate 
1798*7c478bd9Sstevel@tonic-gate 	/*
1799*7c478bd9Sstevel@tonic-gate 	 * The lock for rfs4_file_t must be held when traversing the
1800*7c478bd9Sstevel@tonic-gate 	 * delegation list but that lock needs to be released to call
1801*7c478bd9Sstevel@tonic-gate 	 * rfs4_revoke_deleg()
1802*7c478bd9Sstevel@tonic-gate 	 * This for loop is set up to check the list for being empty,
1803*7c478bd9Sstevel@tonic-gate 	 * and locking the rfs4_file_t struct on init and end
1804*7c478bd9Sstevel@tonic-gate 	 */
1805*7c478bd9Sstevel@tonic-gate 	for (rfs4_dbe_lock(fp->dbe);
1806*7c478bd9Sstevel@tonic-gate 		&fp->delegationlist != fp->delegationlist.next;
1807*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(fp->dbe)) {
1808*7c478bd9Sstevel@tonic-gate 
1809*7c478bd9Sstevel@tonic-gate 		dsp = fp->delegationlist.next->dsp;
1810*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_hold(dsp->dbe);
1811*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(fp->dbe);
1812*7c478bd9Sstevel@tonic-gate 		rfs4_revoke_deleg(dsp);
1813*7c478bd9Sstevel@tonic-gate 		rfs4_deleg_state_rele(dsp);
1814*7c478bd9Sstevel@tonic-gate 	}
1815*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(fp->dbe);
1816*7c478bd9Sstevel@tonic-gate }
1817*7c478bd9Sstevel@tonic-gate 
1818*7c478bd9Sstevel@tonic-gate /*
1819*7c478bd9Sstevel@tonic-gate  * A delegation is assumed to be present on the file associated with
1820*7c478bd9Sstevel@tonic-gate  * "state".  Check to see if the delegation matches is associated with
1821*7c478bd9Sstevel@tonic-gate  * the same client as referenced by "state".  If it is not, TRUE is
1822*7c478bd9Sstevel@tonic-gate  * returned.  If the delegation DOES match the client (or no
1823*7c478bd9Sstevel@tonic-gate  * delegation is present), return FALSE.
1824*7c478bd9Sstevel@tonic-gate  * Assume the state entry and file entry are locked.
1825*7c478bd9Sstevel@tonic-gate  */
1826*7c478bd9Sstevel@tonic-gate bool_t
1827*7c478bd9Sstevel@tonic-gate rfs4_is_deleg(rfs4_state_t *state)
1828*7c478bd9Sstevel@tonic-gate {
1829*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
1830*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = state->finfo;
1831*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp = state->owner->client;
1832*7c478bd9Sstevel@tonic-gate 
1833*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_deleg_debug,
1834*7c478bd9Sstevel@tonic-gate 		(CE_NOTE, "rfs4_is_deleg enter: cp = %p", (void*)cp));
1835*7c478bd9Sstevel@tonic-gate 
1836*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_dbe_islocked(fp->dbe));
1837*7c478bd9Sstevel@tonic-gate 	for (dsp = fp->delegationlist.next->dsp; dsp != NULL;
1838*7c478bd9Sstevel@tonic-gate 		dsp = dsp->delegationlist.next->dsp) {
1839*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_deleg_debug,
1840*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "rfs4_is_deleg: client = %p",
1841*7c478bd9Sstevel@tonic-gate 			(void*)dsp->client));
1842*7c478bd9Sstevel@tonic-gate 		if (cp != dsp->client) {
1843*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_deleg_debug,
1844*7c478bd9Sstevel@tonic-gate 				(CE_NOTE, "rfs4_is_deleg is true"));
1845*7c478bd9Sstevel@tonic-gate 			return (TRUE);
1846*7c478bd9Sstevel@tonic-gate 		}
1847*7c478bd9Sstevel@tonic-gate 	}
1848*7c478bd9Sstevel@tonic-gate 	return (FALSE);
1849*7c478bd9Sstevel@tonic-gate }
1850*7c478bd9Sstevel@tonic-gate 
1851*7c478bd9Sstevel@tonic-gate void
1852*7c478bd9Sstevel@tonic-gate rfs4_disable_delegation(void)
1853*7c478bd9Sstevel@tonic-gate {
1854*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rfs4_deleg_lock);
1855*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_disabled++;
1856*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rfs4_deleg_lock);
1857*7c478bd9Sstevel@tonic-gate }
1858*7c478bd9Sstevel@tonic-gate 
1859*7c478bd9Sstevel@tonic-gate void
1860*7c478bd9Sstevel@tonic-gate rfs4_enable_delegation(void)
1861*7c478bd9Sstevel@tonic-gate {
1862*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rfs4_deleg_lock);
1863*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_deleg_disabled > 0);
1864*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_disabled--;
1865*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rfs4_deleg_lock);
1866*7c478bd9Sstevel@tonic-gate }
1867*7c478bd9Sstevel@tonic-gate 
1868*7c478bd9Sstevel@tonic-gate void
1869*7c478bd9Sstevel@tonic-gate rfs4_mon_hold(void *arg)
1870*7c478bd9Sstevel@tonic-gate {
1871*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = arg;
1872*7c478bd9Sstevel@tonic-gate 
1873*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_hold(fp->dbe);
1874*7c478bd9Sstevel@tonic-gate }
1875*7c478bd9Sstevel@tonic-gate 
1876*7c478bd9Sstevel@tonic-gate void
1877*7c478bd9Sstevel@tonic-gate rfs4_mon_rele(void *arg)
1878*7c478bd9Sstevel@tonic-gate {
1879*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = arg;
1880*7c478bd9Sstevel@tonic-gate 
1881*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_rele_nolock(fp->dbe);
1882*7c478bd9Sstevel@tonic-gate }
1883