1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 2009 Rick Macklem, University of Guelph
3191783Srmacklem * All rights reserved.
4191783Srmacklem *
5191783Srmacklem * Redistribution and use in source and binary forms, with or without
6191783Srmacklem * modification, are permitted provided that the following conditions
7191783Srmacklem * are met:
8191783Srmacklem * 1. Redistributions of source code must retain the above copyright
9191783Srmacklem *    notice, this list of conditions and the following disclaimer.
10191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
11191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
12191783Srmacklem *    documentation and/or other materials provided with the distribution.
13191783Srmacklem *
14191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24191783Srmacklem * SUCH DAMAGE.
25191783Srmacklem *
26191783Srmacklem */
27191783Srmacklem
28191783Srmacklem#include <sys/cdefs.h>
29191783Srmacklem__FBSDID("$FreeBSD$");
30191783Srmacklem
31191783Srmacklem/*
32191783Srmacklem * These functions implement the client side state handling for NFSv4.
33191783Srmacklem * NFSv4 state handling:
34191783Srmacklem * - A lockowner is used to determine lock contention, so it
35191783Srmacklem *   corresponds directly to a Posix pid. (1 to 1 mapping)
36191783Srmacklem * - The correct granularity of an OpenOwner is not nearly so
37191783Srmacklem *   obvious. An OpenOwner does the following:
38191783Srmacklem *   - provides a serial sequencing of Open/Close/Lock-with-new-lockowner
39198289Sjh *   - is used to check for Open/Share contention (not applicable to
40191783Srmacklem *     this client, since all Opens are Deny_None)
41198289Sjh *   As such, I considered both extreme.
42191783Srmacklem *   1 OpenOwner per ClientID - Simple to manage, but fully serializes
43191783Srmacklem *   all Open, Close and Lock (with a new lockowner) Ops.
44191783Srmacklem *   1 OpenOwner for each Open - This one results in an OpenConfirm for
45191783Srmacklem *   every Open, for most servers.
46191783Srmacklem *   So, I chose to use the same mapping as I did for LockOwnwers.
47191783Srmacklem *   The main concern here is that you can end up with multiple Opens
48191783Srmacklem *   for the same File Handle, but on different OpenOwners (opens
49191783Srmacklem *   inherited from parents, grandparents...) and you do not know
50191783Srmacklem *   which of these the vnodeop close applies to. This is handled by
51191783Srmacklem *   delaying the Close Op(s) until all of the Opens have been closed.
52191783Srmacklem *   (It is not yet obvious if this is the correct granularity.)
53198289Sjh * - How the code handles serialization:
54198289Sjh *   - For the ClientId, it uses an exclusive lock while getting its
55191783Srmacklem *     SetClientId and during recovery. Otherwise, it uses a shared
56191783Srmacklem *     lock via a reference count.
57191783Srmacklem *   - For the rest of the data structures, it uses an SMP mutex
58191783Srmacklem *     (once the nfs client is SMP safe) and doesn't sleep while
59191783Srmacklem *     manipulating the linked lists.
60191783Srmacklem *   - The serialization of Open/Close/Lock/LockU falls out in the
61191783Srmacklem *     "wash", since OpenOwners and LockOwners are both mapped from
62191783Srmacklem *     Posix pid. In other words, there is only one Posix pid using
63191783Srmacklem *     any given owner, so that owner is serialized. (If you change
64191783Srmacklem *     the granularity of the OpenOwner, then code must be added to
65191783Srmacklem *     serialize Ops on the OpenOwner.)
66191783Srmacklem * - When to get rid of OpenOwners and LockOwners.
67229599Srmacklem *   - The function nfscl_cleanup_common() is executed after a process exits.
68229599Srmacklem *     It goes through the client list looking for all Open and Lock Owners.
69191783Srmacklem *     When one is found, it is marked "defunct" or in the case of
70191783Srmacklem *     an OpenOwner without any Opens, freed.
71191783Srmacklem *     The renew thread scans for defunct Owners and gets rid of them,
72191783Srmacklem *     if it can. The LockOwners will also be deleted when the
73191783Srmacklem *     associated Open is closed.
74191783Srmacklem *   - If the LockU or Close Op(s) fail during close in a way
75191783Srmacklem *     that could be recovered upon retry, they are relinked to the
76191783Srmacklem *     ClientId's defunct open list and retried by the renew thread
77191783Srmacklem *     until they succeed or an unmount/recovery occurs.
78191783Srmacklem *     (Since we are done with them, they do not need to be recovered.)
79191783Srmacklem */
80191783Srmacklem
81191783Srmacklem#ifndef APPLEKEXT
82191783Srmacklem#include <fs/nfs/nfsport.h>
83191783Srmacklem
84191783Srmacklem/*
85191783Srmacklem * Global variables
86191783Srmacklem */
87191783Srmacklemextern struct nfsstats newnfsstats;
88191783Srmacklemextern struct nfsreqhead nfsd_reqq;
89191783SrmacklemNFSREQSPINLOCK;
90191783SrmacklemNFSCLSTATEMUTEX;
91191783Srmacklemint nfscl_inited = 0;
92191783Srmacklemstruct nfsclhead nfsclhead;	/* Head of clientid list */
93191783Srmacklemint nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
94191783Srmacklem#endif	/* !APPLEKEXT */
95191783Srmacklem
96191783Srmacklemstatic int nfscl_delegcnt = 0;
97191783Srmacklemstatic int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *,
98223774Srmacklem    u_int8_t *, u_int32_t, struct nfscllockowner **, struct nfsclopen **);
99191783Srmacklemstatic void nfscl_clrelease(struct nfsclclient *);
100191783Srmacklemstatic void nfscl_cleanclient(struct nfsclclient *);
101191783Srmacklemstatic void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
102191783Srmacklem    struct ucred *, NFSPROC_T *);
103191783Srmacklemstatic int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
104191783Srmacklem    struct nfsmount *, struct ucred *, NFSPROC_T *);
105191783Srmacklemstatic void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *);
106191783Srmacklemstatic void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
107191783Srmacklem    struct nfscllock *, int);
108191783Srmacklemstatic int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
109191783Srmacklem    struct nfscllock **, int);
110191783Srmacklemstatic void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *);
111191783Srmacklemstatic u_int32_t nfscl_nextcbident(void);
112191783Srmacklemstatic mount_t nfscl_getmnt(u_int32_t);
113191783Srmacklemstatic struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *,
114191783Srmacklem    int);
115191783Srmacklemstatic int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *,
116191783Srmacklem    u_int8_t *, struct nfscllock **);
117191783Srmacklemstatic void nfscl_freealllocks(struct nfscllockownerhead *, int);
118201439Srmacklemstatic int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int,
119201439Srmacklem    struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **);
120191783Srmacklemstatic void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *,
121191783Srmacklem    struct nfsclowner **, struct nfsclowner **, struct nfsclopen **,
122191783Srmacklem    struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *);
123191783Srmacklemstatic int nfscl_moveopen(vnode_t , struct nfsclclient *,
124191783Srmacklem    struct nfsmount *, struct nfsclopen *, struct nfsclowner *,
125191783Srmacklem    struct nfscldeleg *, struct ucred *, NFSPROC_T *);
126191783Srmacklemstatic void nfscl_totalrecall(struct nfsclclient *);
127191783Srmacklemstatic int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *,
128191783Srmacklem    struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *);
129191783Srmacklemstatic int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int,
130191783Srmacklem    u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int,
131191783Srmacklem    struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *);
132191783Srmacklemstatic int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
133191783Srmacklem    int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short,
134191783Srmacklem    struct ucred *, NFSPROC_T *);
135191783Srmacklemstatic int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
136191783Srmacklem    struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
137191783Srmacklemstatic void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *);
138191783Srmacklemstatic int nfscl_errmap(struct nfsrv_descript *);
139191783Srmacklemstatic void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
140191783Srmacklemstatic int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
141207082Srmacklem    struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int);
142191783Srmacklemstatic void nfscl_freeopenowner(struct nfsclowner *, int);
143191783Srmacklemstatic void nfscl_cleandeleg(struct nfscldeleg *);
144191783Srmacklemstatic int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
145191783Srmacklem    struct nfsmount *, NFSPROC_T *);
146229752Srmacklemstatic void nfscl_emptylockowner(struct nfscllockowner *,
147229752Srmacklem    struct nfscllockownerfhhead *);
148191783Srmacklem
149191783Srmacklemstatic short nfscberr_null[] = {
150191783Srmacklem	0,
151191783Srmacklem	0,
152191783Srmacklem};
153191783Srmacklem
154191783Srmacklemstatic short nfscberr_getattr[] = {
155191783Srmacklem	NFSERR_RESOURCE,
156191783Srmacklem	NFSERR_BADHANDLE,
157191783Srmacklem	NFSERR_BADXDR,
158191783Srmacklem	NFSERR_RESOURCE,
159191783Srmacklem	NFSERR_SERVERFAULT,
160191783Srmacklem	0,
161191783Srmacklem};
162191783Srmacklem
163191783Srmacklemstatic short nfscberr_recall[] = {
164191783Srmacklem	NFSERR_RESOURCE,
165191783Srmacklem	NFSERR_BADHANDLE,
166191783Srmacklem	NFSERR_BADSTATEID,
167191783Srmacklem	NFSERR_BADXDR,
168191783Srmacklem	NFSERR_RESOURCE,
169191783Srmacklem	NFSERR_SERVERFAULT,
170191783Srmacklem	0,
171191783Srmacklem};
172191783Srmacklem
173191783Srmacklemstatic short *nfscl_cberrmap[] = {
174191783Srmacklem	nfscberr_null,
175191783Srmacklem	nfscberr_null,
176191783Srmacklem	nfscberr_null,
177191783Srmacklem	nfscberr_getattr,
178191783Srmacklem	nfscberr_recall
179191783Srmacklem};
180191783Srmacklem
181191783Srmacklem#define	NETFAMILY(clp) \
182191783Srmacklem		(((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET)
183191783Srmacklem
184191783Srmacklem/*
185191783Srmacklem * Called for an open operation.
186191783Srmacklem * If the nfhp argument is NULL, just get an openowner.
187191783Srmacklem */
188191783SrmacklemAPPLESTATIC int
189191783Srmacklemnfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg,
190191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp,
191191783Srmacklem    struct nfsclopen **opp, int *newonep, int *retp, int lockit)
192191783Srmacklem{
193191783Srmacklem	struct nfsclclient *clp;
194191783Srmacklem	struct nfsclowner *owp, *nowp;
195191783Srmacklem	struct nfsclopen *op = NULL, *nop = NULL;
196191783Srmacklem	struct nfscldeleg *dp;
197191783Srmacklem	struct nfsclownerhead *ohp;
198191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
199191783Srmacklem	int ret;
200191783Srmacklem
201191783Srmacklem	if (newonep != NULL)
202191783Srmacklem		*newonep = 0;
203191783Srmacklem	if (opp != NULL)
204191783Srmacklem		*opp = NULL;
205191783Srmacklem	if (owpp != NULL)
206191783Srmacklem		*owpp = NULL;
207191783Srmacklem
208191783Srmacklem	/*
209191783Srmacklem	 * Might need one or both of these, so MALLOC them now, to
210191783Srmacklem	 * avoid a tsleep() in MALLOC later.
211191783Srmacklem	 */
212191783Srmacklem	MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner),
213191783Srmacklem	    M_NFSCLOWNER, M_WAITOK);
214191783Srmacklem	if (nfhp != NULL)
215191783Srmacklem	    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
216191783Srmacklem		fhlen - 1, M_NFSCLOPEN, M_WAITOK);
217191783Srmacklem	ret = nfscl_getcl(vp, cred, p, &clp);
218191783Srmacklem	if (ret != 0) {
219191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
220191783Srmacklem		if (nop != NULL)
221191783Srmacklem			FREE((caddr_t)nop, M_NFSCLOPEN);
222191783Srmacklem		return (ret);
223191783Srmacklem	}
224191783Srmacklem
225191783Srmacklem	/*
226191783Srmacklem	 * Get the Open iff it already exists.
227191783Srmacklem	 * If none found, add the new one or return error, depending upon
228191783Srmacklem	 * "create".
229191783Srmacklem	 */
230222719Srmacklem	nfscl_filllockowner(p->td_proc, own, F_POSIX);
231191783Srmacklem	NFSLOCKCLSTATE();
232191783Srmacklem	dp = NULL;
233191783Srmacklem	/* First check the delegation list */
234191783Srmacklem	if (nfhp != NULL && usedeleg) {
235191783Srmacklem		LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
236191783Srmacklem			if (dp->nfsdl_fhlen == fhlen &&
237191783Srmacklem			    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
238191783Srmacklem				if (!(amode & NFSV4OPEN_ACCESSWRITE) ||
239191783Srmacklem				    (dp->nfsdl_flags & NFSCLDL_WRITE))
240191783Srmacklem					break;
241191783Srmacklem				dp = NULL;
242191783Srmacklem				break;
243191783Srmacklem			}
244191783Srmacklem		}
245191783Srmacklem	}
246191783Srmacklem
247191783Srmacklem	if (dp != NULL)
248191783Srmacklem		ohp = &dp->nfsdl_owner;
249191783Srmacklem	else
250191783Srmacklem		ohp = &clp->nfsc_owner;
251191783Srmacklem	/* Now, search for an openowner */
252191783Srmacklem	LIST_FOREACH(owp, ohp, nfsow_list) {
253191783Srmacklem		if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN))
254191783Srmacklem			break;
255191783Srmacklem	}
256191783Srmacklem
257191783Srmacklem	/*
258191783Srmacklem	 * Create a new open, as required.
259191783Srmacklem	 */
260191783Srmacklem	nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen,
261191783Srmacklem	    newonep);
262191783Srmacklem
263191783Srmacklem	/*
264191783Srmacklem	 * Serialize modifications to the open owner for multiple threads
265191783Srmacklem	 * within the same process using a read/write sleep lock.
266191783Srmacklem	 */
267191783Srmacklem	if (lockit)
268191783Srmacklem		nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
269191783Srmacklem	NFSUNLOCKCLSTATE();
270191783Srmacklem	if (nowp != NULL)
271191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
272191783Srmacklem	if (nop != NULL)
273191783Srmacklem		FREE((caddr_t)nop, M_NFSCLOPEN);
274191783Srmacklem	if (owpp != NULL)
275191783Srmacklem		*owpp = owp;
276191783Srmacklem	if (opp != NULL)
277191783Srmacklem		*opp = op;
278206688Srmacklem	if (retp != NULL) {
279206688Srmacklem		if (nfhp != NULL && dp != NULL && nop == NULL)
280206688Srmacklem			/* new local open on delegation */
281206688Srmacklem			*retp = NFSCLOPEN_SETCRED;
282206688Srmacklem		else
283206688Srmacklem			*retp = NFSCLOPEN_OK;
284206688Srmacklem	}
285191783Srmacklem
286191783Srmacklem	/*
287191783Srmacklem	 * Now, check the mode on the open and return the appropriate
288191783Srmacklem	 * value.
289191783Srmacklem	 */
290191783Srmacklem	if (op != NULL && (amode & ~(op->nfso_mode))) {
291191783Srmacklem		op->nfso_mode |= amode;
292191783Srmacklem		if (retp != NULL && dp == NULL)
293191783Srmacklem			*retp = NFSCLOPEN_DOOPEN;
294191783Srmacklem	}
295191783Srmacklem	return (0);
296191783Srmacklem}
297191783Srmacklem
298191783Srmacklem/*
299191783Srmacklem * Create a new open, as required.
300191783Srmacklem */
301191783Srmacklemstatic void
302191783Srmacklemnfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp,
303191783Srmacklem    struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp,
304191783Srmacklem    struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen,
305191783Srmacklem    int *newonep)
306191783Srmacklem{
307191783Srmacklem	struct nfsclowner *owp = *owpp, *nowp;
308191783Srmacklem	struct nfsclopen *op, *nop;
309191783Srmacklem
310191783Srmacklem	if (nowpp != NULL)
311191783Srmacklem		nowp = *nowpp;
312191783Srmacklem	else
313191783Srmacklem		nowp = NULL;
314191783Srmacklem	if (nopp != NULL)
315191783Srmacklem		nop = *nopp;
316191783Srmacklem	else
317191783Srmacklem		nop = NULL;
318191783Srmacklem	if (owp == NULL && nowp != NULL) {
319191783Srmacklem		NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
320191783Srmacklem		LIST_INIT(&nowp->nfsow_open);
321191783Srmacklem		nowp->nfsow_clp = clp;
322191783Srmacklem		nowp->nfsow_seqid = 0;
323191783Srmacklem		nowp->nfsow_defunct = 0;
324191783Srmacklem		nfscl_lockinit(&nowp->nfsow_rwlock);
325191783Srmacklem		if (dp != NULL) {
326191783Srmacklem			newnfsstats.cllocalopenowners++;
327191783Srmacklem			LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list);
328191783Srmacklem		} else {
329191783Srmacklem			newnfsstats.clopenowners++;
330191783Srmacklem			LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list);
331191783Srmacklem		}
332191783Srmacklem		owp = *owpp = nowp;
333191783Srmacklem		*nowpp = NULL;
334191783Srmacklem		if (newonep != NULL)
335191783Srmacklem			*newonep = 1;
336191783Srmacklem	}
337191783Srmacklem
338191783Srmacklem	 /* If an fhp has been specified, create an Open as well. */
339191783Srmacklem	if (fhp != NULL) {
340191783Srmacklem		/* and look for the correct open, based upon FH */
341191783Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
342191783Srmacklem			if (op->nfso_fhlen == fhlen &&
343191783Srmacklem			    !NFSBCMP(op->nfso_fh, fhp, fhlen))
344191783Srmacklem				break;
345191783Srmacklem		}
346191783Srmacklem		if (op == NULL && nop != NULL) {
347191783Srmacklem			nop->nfso_own = owp;
348191783Srmacklem			nop->nfso_mode = 0;
349191783Srmacklem			nop->nfso_opencnt = 0;
350191783Srmacklem			nop->nfso_posixlock = 1;
351191783Srmacklem			nop->nfso_fhlen = fhlen;
352191783Srmacklem			NFSBCOPY(fhp, nop->nfso_fh, fhlen);
353191783Srmacklem			LIST_INIT(&nop->nfso_lock);
354191783Srmacklem			nop->nfso_stateid.seqid = 0;
355191783Srmacklem			nop->nfso_stateid.other[0] = 0;
356191783Srmacklem			nop->nfso_stateid.other[1] = 0;
357191783Srmacklem			nop->nfso_stateid.other[2] = 0;
358191783Srmacklem			if (dp != NULL) {
359191783Srmacklem				TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
360191783Srmacklem				TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
361191783Srmacklem				    nfsdl_list);
362191783Srmacklem				dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
363191783Srmacklem				newnfsstats.cllocalopens++;
364191783Srmacklem			} else {
365191783Srmacklem				newnfsstats.clopens++;
366191783Srmacklem			}
367191783Srmacklem			LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list);
368191783Srmacklem			*opp = nop;
369191783Srmacklem			*nopp = NULL;
370191783Srmacklem			if (newonep != NULL)
371191783Srmacklem				*newonep = 1;
372191783Srmacklem		} else {
373191783Srmacklem			*opp = op;
374191783Srmacklem		}
375191783Srmacklem	}
376191783Srmacklem}
377191783Srmacklem
378191783Srmacklem/*
379191783Srmacklem * Called to find/add a delegation to a client.
380191783Srmacklem */
381191783SrmacklemAPPLESTATIC int
382191783Srmacklemnfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
383191783Srmacklem    int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp)
384191783Srmacklem{
385191783Srmacklem	struct nfscldeleg *dp = *dpp, *tdp;
386191783Srmacklem
387191783Srmacklem	/*
388191783Srmacklem	 * First, if we have received a Read delegation for a file on a
389191783Srmacklem	 * read/write file system, just return it, because they aren't
390191783Srmacklem	 * useful, imho.
391191783Srmacklem	 */
392191783Srmacklem	if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) &&
393191783Srmacklem	    (dp->nfsdl_flags & NFSCLDL_READ)) {
394191783Srmacklem		(void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p);
395191783Srmacklem		FREE((caddr_t)dp, M_NFSCLDELEG);
396191783Srmacklem		*dpp = NULL;
397191783Srmacklem		return (0);
398191783Srmacklem	}
399191783Srmacklem
400191783Srmacklem	/* Look for the correct deleg, based upon FH */
401191783Srmacklem	NFSLOCKCLSTATE();
402191783Srmacklem	tdp = nfscl_finddeleg(clp, nfhp, fhlen);
403191783Srmacklem	if (tdp == NULL) {
404191783Srmacklem		if (dp == NULL) {
405191783Srmacklem			NFSUNLOCKCLSTATE();
406191783Srmacklem			return (NFSERR_BADSTATEID);
407191783Srmacklem		}
408191783Srmacklem		*dpp = NULL;
409191783Srmacklem		TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
410191783Srmacklem		LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp,
411191783Srmacklem		    nfsdl_hash);
412191783Srmacklem		dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
413191783Srmacklem		newnfsstats.cldelegates++;
414191783Srmacklem		nfscl_delegcnt++;
415191783Srmacklem	} else {
416191783Srmacklem		/*
417191783Srmacklem		 * Delegation already exists, what do we do if a new one??
418191783Srmacklem		 */
419191783Srmacklem		if (dp != NULL) {
420191783Srmacklem			printf("Deleg already exists!\n");
421191783Srmacklem			FREE((caddr_t)dp, M_NFSCLDELEG);
422191783Srmacklem			*dpp = NULL;
423191783Srmacklem		} else {
424191783Srmacklem			*dpp = tdp;
425191783Srmacklem		}
426191783Srmacklem	}
427191783Srmacklem	NFSUNLOCKCLSTATE();
428191783Srmacklem	return (0);
429191783Srmacklem}
430191783Srmacklem
431191783Srmacklem/*
432191783Srmacklem * Find a delegation for this file handle. Return NULL upon failure.
433191783Srmacklem */
434191783Srmacklemstatic struct nfscldeleg *
435191783Srmacklemnfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
436191783Srmacklem{
437191783Srmacklem	struct nfscldeleg *dp;
438191783Srmacklem
439191783Srmacklem	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) {
440191783Srmacklem	    if (dp->nfsdl_fhlen == fhlen &&
441191783Srmacklem		!NFSBCMP(dp->nfsdl_fh, fhp, fhlen))
442191783Srmacklem		break;
443191783Srmacklem	}
444191783Srmacklem	return (dp);
445191783Srmacklem}
446191783Srmacklem
447191783Srmacklem/*
448191783Srmacklem * Get a stateid for an I/O operation. First, look for an open and iff
449191783Srmacklem * found, return either a lockowner stateid or the open stateid.
450191783Srmacklem * If no Open is found, just return error and the special stateid of all zeros.
451191783Srmacklem */
452191783SrmacklemAPPLESTATIC int
453191783Srmacklemnfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
454191783Srmacklem    struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp,
455191783Srmacklem    void **lckpp)
456191783Srmacklem{
457191783Srmacklem	struct nfsclclient *clp;
458191783Srmacklem	struct nfsclowner *owp;
459195510Srmacklem	struct nfsclopen *op = NULL;
460191783Srmacklem	struct nfscllockowner *lp;
461191783Srmacklem	struct nfscldeleg *dp;
462191783Srmacklem	struct nfsnode *np;
463191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
464191783Srmacklem	int error, done;
465191783Srmacklem
466191783Srmacklem	*lckpp = NULL;
467191783Srmacklem	/*
468191783Srmacklem	 * Initially, just set the special stateid of all zeros.
469191783Srmacklem	 */
470191783Srmacklem	stateidp->seqid = 0;
471191783Srmacklem	stateidp->other[0] = 0;
472191783Srmacklem	stateidp->other[1] = 0;
473191783Srmacklem	stateidp->other[2] = 0;
474191783Srmacklem	if (vnode_vtype(vp) != VREG)
475191783Srmacklem		return (EISDIR);
476191783Srmacklem	np = VTONFS(vp);
477191783Srmacklem	NFSLOCKCLSTATE();
478191783Srmacklem	clp = nfscl_findcl(VFSTONFS(vnode_mount(vp)));
479191783Srmacklem	if (clp == NULL) {
480191783Srmacklem		NFSUNLOCKCLSTATE();
481191783Srmacklem		return (EACCES);
482191783Srmacklem	}
483191783Srmacklem
484191783Srmacklem	/*
485206818Srmacklem	 * Wait for recovery to complete.
486206818Srmacklem	 */
487206818Srmacklem	while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG))
488206818Srmacklem		(void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR,
489206818Srmacklem		    PZERO, "nfsrecvr", NULL);
490206818Srmacklem
491206818Srmacklem	/*
492191783Srmacklem	 * First, look for a delegation.
493191783Srmacklem	 */
494191783Srmacklem	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
495191783Srmacklem		if (dp->nfsdl_fhlen == fhlen &&
496191783Srmacklem		    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
497191783Srmacklem			if (!(mode & NFSV4OPEN_ACCESSWRITE) ||
498191783Srmacklem			    (dp->nfsdl_flags & NFSCLDL_WRITE)) {
499191783Srmacklem				stateidp->seqid = dp->nfsdl_stateid.seqid;
500191783Srmacklem				stateidp->other[0] = dp->nfsdl_stateid.other[0];
501191783Srmacklem				stateidp->other[1] = dp->nfsdl_stateid.other[1];
502191783Srmacklem				stateidp->other[2] = dp->nfsdl_stateid.other[2];
503191783Srmacklem				if (!(np->n_flag & NDELEGRECALL)) {
504191783Srmacklem					TAILQ_REMOVE(&clp->nfsc_deleg, dp,
505191783Srmacklem					    nfsdl_list);
506191783Srmacklem					TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
507191783Srmacklem					    nfsdl_list);
508191783Srmacklem					dp->nfsdl_timestamp = NFSD_MONOSEC +
509191783Srmacklem					    120;
510191783Srmacklem					dp->nfsdl_rwlock.nfslock_usecnt++;
511191783Srmacklem					*lckpp = (void *)&dp->nfsdl_rwlock;
512191783Srmacklem				}
513191783Srmacklem				NFSUNLOCKCLSTATE();
514191783Srmacklem				return (0);
515191783Srmacklem			}
516191783Srmacklem			break;
517191783Srmacklem		}
518191783Srmacklem	}
519191783Srmacklem
520191783Srmacklem	if (p != NULL) {
521191783Srmacklem		/*
522191783Srmacklem		 * If p != NULL, we want to search the parentage tree
523191783Srmacklem		 * for a matching OpenOwner and use that.
524191783Srmacklem		 */
525222719Srmacklem		nfscl_filllockowner(p->td_proc, own, F_POSIX);
526223774Srmacklem		lp = NULL;
527223774Srmacklem		error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, own, own,
528223774Srmacklem		    mode, &lp, &op);
529223774Srmacklem		if (error == 0 && lp != NULL) {
530223774Srmacklem			stateidp->seqid =
531223774Srmacklem			    lp->nfsl_stateid.seqid;
532223774Srmacklem			stateidp->other[0] =
533223774Srmacklem			    lp->nfsl_stateid.other[0];
534223774Srmacklem			stateidp->other[1] =
535223774Srmacklem			    lp->nfsl_stateid.other[1];
536223774Srmacklem			stateidp->other[2] =
537223774Srmacklem			    lp->nfsl_stateid.other[2];
538223774Srmacklem			NFSUNLOCKCLSTATE();
539223774Srmacklem			return (0);
540191783Srmacklem		}
541195510Srmacklem	}
542195510Srmacklem	if (op == NULL) {
543195510Srmacklem		/* If not found, just look for any OpenOwner that will work. */
544191783Srmacklem		done = 0;
545191783Srmacklem		owp = LIST_FIRST(&clp->nfsc_owner);
546191783Srmacklem		while (!done && owp != NULL) {
547195510Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
548195510Srmacklem				if (op->nfso_fhlen == fhlen &&
549195510Srmacklem				    !NFSBCMP(op->nfso_fh, nfhp, fhlen) &&
550195510Srmacklem				    (mode & op->nfso_mode) == mode) {
551195510Srmacklem					done = 1;
552195510Srmacklem					break;
553195510Srmacklem				}
554191783Srmacklem			}
555195510Srmacklem			if (!done)
556195510Srmacklem				owp = LIST_NEXT(owp, nfsow_list);
557191783Srmacklem		}
558191783Srmacklem		if (!done) {
559191783Srmacklem			NFSUNLOCKCLSTATE();
560191783Srmacklem			return (ENOENT);
561191783Srmacklem		}
562231545Srmacklem		/*
563231545Srmacklem		 * For read aheads or write behinds, use the open cred.
564231545Srmacklem		 * A read ahead or write behind is indicated by p == NULL.
565231545Srmacklem		 */
566231545Srmacklem		if (p == NULL)
567231545Srmacklem			newnfs_copycred(&op->nfso_cred, cred);
568191783Srmacklem	}
569191783Srmacklem
570191783Srmacklem	/*
571191783Srmacklem	 * No lock stateid, so return the open stateid.
572191783Srmacklem	 */
573191783Srmacklem	stateidp->seqid = op->nfso_stateid.seqid;
574191783Srmacklem	stateidp->other[0] = op->nfso_stateid.other[0];
575191783Srmacklem	stateidp->other[1] = op->nfso_stateid.other[1];
576191783Srmacklem	stateidp->other[2] = op->nfso_stateid.other[2];
577191783Srmacklem	NFSUNLOCKCLSTATE();
578191783Srmacklem	return (0);
579191783Srmacklem}
580191783Srmacklem
581191783Srmacklem/*
582223774Srmacklem * Search for a matching file, mode and, optionally, lockowner.
583191783Srmacklem */
584191783Srmacklemstatic int
585191783Srmacklemnfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen,
586223774Srmacklem    u_int8_t *openown, u_int8_t *lockown, u_int32_t mode,
587223774Srmacklem    struct nfscllockowner **lpp, struct nfsclopen **opp)
588191783Srmacklem{
589223774Srmacklem	struct nfsclowner *owp;
590223774Srmacklem	struct nfsclopen *op, *rop, *rop2;
591223774Srmacklem	struct nfscllockowner *lp;
592223774Srmacklem	int keep_looping;
593191783Srmacklem
594223774Srmacklem	if (lpp != NULL)
595223774Srmacklem		*lpp = NULL;
596223774Srmacklem	/*
597223774Srmacklem	 * rop will be set to the open to be returned. There are three
598223774Srmacklem	 * variants of this, all for an open of the correct file:
599223774Srmacklem	 * 1 - A match of lockown.
600223774Srmacklem	 * 2 - A match of the openown, when no lockown match exists.
601223774Srmacklem	 * 3 - A match for any open, if no openown or lockown match exists.
602223774Srmacklem	 * Looking for #2 over #3 probably isn't necessary, but since
603223774Srmacklem	 * RFC3530 is vague w.r.t. the relationship between openowners and
604223774Srmacklem	 * lockowners, I think this is the safer way to go.
605223774Srmacklem	 */
606223774Srmacklem	rop = NULL;
607223774Srmacklem	rop2 = NULL;
608223774Srmacklem	keep_looping = 1;
609223774Srmacklem	/* Search the client list */
610223774Srmacklem	owp = LIST_FIRST(ohp);
611223774Srmacklem	while (owp != NULL && keep_looping != 0) {
612223774Srmacklem		/* and look for the correct open */
613223774Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
614223774Srmacklem		while (op != NULL && keep_looping != 0) {
615223774Srmacklem			if (op->nfso_fhlen == fhlen &&
616223774Srmacklem			    !NFSBCMP(op->nfso_fh, nfhp, fhlen)
617223774Srmacklem			    && (op->nfso_mode & mode) == mode) {
618223774Srmacklem				if (lpp != NULL) {
619223774Srmacklem					/* Now look for a matching lockowner. */
620223774Srmacklem					LIST_FOREACH(lp, &op->nfso_lock,
621223774Srmacklem					    nfsl_list) {
622223774Srmacklem						if (!NFSBCMP(lp->nfsl_owner,
623223774Srmacklem						    lockown,
624223774Srmacklem						    NFSV4CL_LOCKNAMELEN)) {
625223774Srmacklem							*lpp = lp;
626223774Srmacklem							rop = op;
627223774Srmacklem							keep_looping = 0;
628223774Srmacklem							break;
629223774Srmacklem						}
630223774Srmacklem					}
631191783Srmacklem				}
632223774Srmacklem				if (rop == NULL && !NFSBCMP(owp->nfsow_owner,
633223774Srmacklem				    openown, NFSV4CL_LOCKNAMELEN)) {
634223774Srmacklem					rop = op;
635223774Srmacklem					if (lpp == NULL)
636223774Srmacklem						keep_looping = 0;
637223774Srmacklem				}
638223774Srmacklem				if (rop2 == NULL)
639223774Srmacklem					rop2 = op;
640191783Srmacklem			}
641223774Srmacklem			op = LIST_NEXT(op, nfso_list);
642191783Srmacklem		}
643223774Srmacklem		owp = LIST_NEXT(owp, nfsow_list);
644191783Srmacklem	}
645223774Srmacklem	if (rop == NULL)
646223774Srmacklem		rop = rop2;
647223774Srmacklem	if (rop == NULL)
648191783Srmacklem		return (EBADF);
649223774Srmacklem	*opp = rop;
650191783Srmacklem	return (0);
651191783Srmacklem}
652191783Srmacklem
653191783Srmacklem/*
654191783Srmacklem * Release use of an open owner. Called when open operations are done
655191783Srmacklem * with the open owner.
656191783Srmacklem */
657191783SrmacklemAPPLESTATIC void
658191783Srmacklemnfscl_ownerrelease(struct nfsclowner *owp, __unused int error,
659191783Srmacklem    __unused int candelete, int unlocked)
660191783Srmacklem{
661191783Srmacklem
662191783Srmacklem	if (owp == NULL)
663191783Srmacklem		return;
664191783Srmacklem	NFSLOCKCLSTATE();
665191783Srmacklem	if (!unlocked)
666191783Srmacklem		nfscl_lockunlock(&owp->nfsow_rwlock);
667191783Srmacklem	nfscl_clrelease(owp->nfsow_clp);
668191783Srmacklem	NFSUNLOCKCLSTATE();
669191783Srmacklem}
670191783Srmacklem
671191783Srmacklem/*
672191783Srmacklem * Release use of an open structure under an open owner.
673191783Srmacklem */
674191783SrmacklemAPPLESTATIC void
675191783Srmacklemnfscl_openrelease(struct nfsclopen *op, int error, int candelete)
676191783Srmacklem{
677191783Srmacklem	struct nfsclclient *clp;
678191783Srmacklem	struct nfsclowner *owp;
679191783Srmacklem
680191783Srmacklem	if (op == NULL)
681191783Srmacklem		return;
682191783Srmacklem	NFSLOCKCLSTATE();
683191783Srmacklem	owp = op->nfso_own;
684191783Srmacklem	nfscl_lockunlock(&owp->nfsow_rwlock);
685191783Srmacklem	clp = owp->nfsow_clp;
686191783Srmacklem	if (error && candelete && op->nfso_opencnt == 0)
687191783Srmacklem		nfscl_freeopen(op, 0);
688191783Srmacklem	nfscl_clrelease(clp);
689191783Srmacklem	NFSUNLOCKCLSTATE();
690191783Srmacklem}
691191783Srmacklem
692191783Srmacklem/*
693191783Srmacklem * Called to get a clientid structure. It will optionally lock the
694191783Srmacklem * client data structures to do the SetClientId/SetClientId_confirm,
695191783Srmacklem * but will release that lock and return the clientid with a refernce
696191783Srmacklem * count on it.
697193735Srmacklem * If the "cred" argument is NULL, a new clientid should not be created.
698193735Srmacklem * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot
699193735Srmacklem * be done.
700192337Srmacklem * It always clpp with a reference count on it, unless returning an error.
701191783Srmacklem */
702191783SrmacklemAPPLESTATIC int
703191783Srmacklemnfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
704191783Srmacklem    struct nfsclclient **clpp)
705191783Srmacklem{
706191783Srmacklem	struct nfsclclient *clp;
707193735Srmacklem	struct nfsclclient *newclp = NULL;
708222389Srmacklem	struct mount *mp;
709222389Srmacklem	struct nfsmount *nmp;
710193066Sjamie	char uuid[HOSTUUIDLEN];
711191783Srmacklem	int igotlock = 0, error, trystalecnt, clidinusedelay, i;
712193735Srmacklem	u_int16_t idlen = 0;
713191783Srmacklem
714222389Srmacklem	mp = vnode_mount(vp);
715222389Srmacklem	nmp = VFSTONFS(mp);
716193735Srmacklem	if (cred != NULL) {
717194117Sjamie		getcredhostuuid(cred, uuid, sizeof uuid);
718193735Srmacklem		idlen = strlen(uuid);
719193735Srmacklem		if (idlen > 0)
720193735Srmacklem			idlen += sizeof (u_int64_t);
721193735Srmacklem		else
722193735Srmacklem			idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
723193735Srmacklem		MALLOC(newclp, struct nfsclclient *,
724193735Srmacklem		    sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
725193735Srmacklem		    M_WAITOK);
726193735Srmacklem	}
727191783Srmacklem	NFSLOCKCLSTATE();
728222389Srmacklem	/*
729222389Srmacklem	 * If a forced dismount is already in progress, don't
730222389Srmacklem	 * allocate a new clientid and get out now. For the case where
731222389Srmacklem	 * clp != NULL, this is a harmless optimization.
732222389Srmacklem	 */
733222389Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
734222389Srmacklem		NFSUNLOCKCLSTATE();
735222389Srmacklem		if (newclp != NULL)
736222389Srmacklem			free(newclp, M_NFSCLCLIENT);
737222389Srmacklem		return (EBADF);
738222389Srmacklem	}
739191783Srmacklem	clp = nmp->nm_clp;
740191783Srmacklem	if (clp == NULL) {
741193735Srmacklem		if (newclp == NULL) {
742193735Srmacklem			NFSUNLOCKCLSTATE();
743193735Srmacklem			return (EACCES);
744193735Srmacklem		}
745191783Srmacklem		clp = newclp;
746191783Srmacklem		NFSBZERO((caddr_t)clp, sizeof(struct nfsclclient) + idlen - 1);
747191783Srmacklem		clp->nfsc_idlen = idlen;
748191783Srmacklem		LIST_INIT(&clp->nfsc_owner);
749191783Srmacklem		TAILQ_INIT(&clp->nfsc_deleg);
750191783Srmacklem		for (i = 0; i < NFSCLDELEGHASHSIZE; i++)
751191783Srmacklem			LIST_INIT(&clp->nfsc_deleghash[i]);
752191783Srmacklem		clp->nfsc_flags = NFSCLFLAGS_INITED;
753191783Srmacklem		clp->nfsc_clientidrev = 1;
754191783Srmacklem		clp->nfsc_cbident = nfscl_nextcbident();
755193066Sjamie		nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id,
756191783Srmacklem		    clp->nfsc_idlen);
757191783Srmacklem		LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list);
758191783Srmacklem		nmp->nm_clp = clp;
759191783Srmacklem		clp->nfsc_nmp = nmp;
760191783Srmacklem		NFSUNLOCKCLSTATE();
761191783Srmacklem		nfscl_start_renewthread(clp);
762191783Srmacklem	} else {
763191783Srmacklem		NFSUNLOCKCLSTATE();
764193735Srmacklem		if (newclp != NULL)
765193735Srmacklem			FREE((caddr_t)newclp, M_NFSCLCLIENT);
766191783Srmacklem	}
767191783Srmacklem	NFSLOCKCLSTATE();
768223971Srmacklem	while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock &&
769223971Srmacklem	    (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0)
770191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
771222389Srmacklem		    NFSCLSTATEMUTEXPTR, mp);
772191783Srmacklem	if (!igotlock)
773222389Srmacklem		nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
774222389Srmacklem	if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
775222389Srmacklem		/*
776222389Srmacklem		 * Both nfsv4_lock() and nfsv4_getref() know to check
777222389Srmacklem		 * for MNTK_UNMOUNTF and return without sleeping to
778222389Srmacklem		 * wait for the exclusive lock to be released, since it
779222389Srmacklem		 * might be held by nfscl_umount() and we need to get out
780222389Srmacklem		 * now for that case and not wait until nfscl_umount()
781222389Srmacklem		 * releases it.
782222389Srmacklem		 */
783222389Srmacklem		NFSUNLOCKCLSTATE();
784222389Srmacklem		return (EBADF);
785222389Srmacklem	}
786191783Srmacklem	NFSUNLOCKCLSTATE();
787191783Srmacklem
788191783Srmacklem	/*
789191783Srmacklem	 * If it needs a clientid, do the setclientid now.
790191783Srmacklem	 */
791191783Srmacklem	if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) {
792191783Srmacklem		if (!igotlock)
793191783Srmacklem			panic("nfscl_clget");
794193735Srmacklem		if (p == NULL || cred == NULL) {
795191783Srmacklem			NFSLOCKCLSTATE();
796191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
797191783Srmacklem			NFSUNLOCKCLSTATE();
798191783Srmacklem			return (EACCES);
799191783Srmacklem		}
800191783Srmacklem		/*
801191783Srmacklem		 * If RFC3530 Sec. 14.2.33 is taken literally,
802191783Srmacklem		 * NFSERR_CLIDINUSE will be returned persistently for the
803191783Srmacklem		 * case where a new mount of the same file system is using
804191783Srmacklem		 * a different principal. In practice, NFSERR_CLIDINUSE is
805191783Srmacklem		 * only returned when there is outstanding unexpired state
806191783Srmacklem		 * on the clientid. As such, try for twice the lease
807191783Srmacklem		 * interval, if we know what that is. Otherwise, make a
808191783Srmacklem		 * wild ass guess.
809191783Srmacklem		 * The case of returning NFSERR_STALECLIENTID is far less
810191783Srmacklem		 * likely, but might occur if there is a significant delay
811191783Srmacklem		 * between doing the SetClientID and SetClientIDConfirm Ops,
812191783Srmacklem		 * such that the server throws away the clientid before
813191783Srmacklem		 * receiving the SetClientIDConfirm.
814191783Srmacklem		 */
815191783Srmacklem		if (clp->nfsc_renew > 0)
816191783Srmacklem			clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
817191783Srmacklem		else
818191783Srmacklem			clidinusedelay = 120;
819191783Srmacklem		trystalecnt = 3;
820191783Srmacklem		do {
821193735Srmacklem			error = nfsrpc_setclient(VFSTONFS(vnode_mount(vp)),
822193735Srmacklem			    clp, cred, p);
823191783Srmacklem			if (error == NFSERR_STALECLIENTID ||
824191783Srmacklem			    error == NFSERR_STALEDONTRECOVER ||
825191783Srmacklem			    error == NFSERR_CLIDINUSE) {
826207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfs_setcl");
827191783Srmacklem			}
828191783Srmacklem		} while (((error == NFSERR_STALECLIENTID ||
829191783Srmacklem		     error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) ||
830191783Srmacklem		    (error == NFSERR_CLIDINUSE && --clidinusedelay > 0));
831191783Srmacklem		if (error) {
832191783Srmacklem			NFSLOCKCLSTATE();
833191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
834191783Srmacklem			NFSUNLOCKCLSTATE();
835191783Srmacklem			return (error);
836191783Srmacklem		}
837191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
838191783Srmacklem	}
839191783Srmacklem	if (igotlock) {
840191783Srmacklem		NFSLOCKCLSTATE();
841191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 1);
842191783Srmacklem		NFSUNLOCKCLSTATE();
843191783Srmacklem	}
844191783Srmacklem
845191783Srmacklem	*clpp = clp;
846191783Srmacklem	return (0);
847191783Srmacklem}
848191783Srmacklem
849191783Srmacklem/*
850191783Srmacklem * Get a reference to a clientid and return it, if valid.
851191783Srmacklem */
852191783SrmacklemAPPLESTATIC struct nfsclclient *
853191783Srmacklemnfscl_findcl(struct nfsmount *nmp)
854191783Srmacklem{
855191783Srmacklem	struct nfsclclient *clp;
856191783Srmacklem
857191783Srmacklem	clp = nmp->nm_clp;
858191783Srmacklem	if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID))
859191783Srmacklem		return (NULL);
860191783Srmacklem	return (clp);
861191783Srmacklem}
862191783Srmacklem
863191783Srmacklem/*
864191783Srmacklem * Release the clientid structure. It may be locked or reference counted.
865191783Srmacklem */
866191783Srmacklemstatic void
867191783Srmacklemnfscl_clrelease(struct nfsclclient *clp)
868191783Srmacklem{
869191783Srmacklem
870191783Srmacklem	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
871191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
872191783Srmacklem	else
873191783Srmacklem		nfsv4_relref(&clp->nfsc_lock);
874191783Srmacklem}
875191783Srmacklem
876191783Srmacklem/*
877191783Srmacklem * External call for nfscl_clrelease.
878191783Srmacklem */
879191783SrmacklemAPPLESTATIC void
880191783Srmacklemnfscl_clientrelease(struct nfsclclient *clp)
881191783Srmacklem{
882191783Srmacklem
883191783Srmacklem	NFSLOCKCLSTATE();
884191783Srmacklem	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
885191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
886191783Srmacklem	else
887191783Srmacklem		nfsv4_relref(&clp->nfsc_lock);
888191783Srmacklem	NFSUNLOCKCLSTATE();
889191783Srmacklem}
890191783Srmacklem
891191783Srmacklem/*
892191783Srmacklem * Called when wanting to lock a byte region.
893191783Srmacklem */
894191783SrmacklemAPPLESTATIC int
895191783Srmacklemnfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
896191783Srmacklem    short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp,
897222719Srmacklem    int recovery, void *id, int flags, u_int8_t *rownp, u_int8_t *ropenownp,
898191783Srmacklem    struct nfscllockowner **lpp, int *newonep, int *donelocallyp)
899191783Srmacklem{
900191783Srmacklem	struct nfscllockowner *lp;
901191783Srmacklem	struct nfsclopen *op;
902191783Srmacklem	struct nfsclclient *clp;
903191783Srmacklem	struct nfscllockowner *nlp;
904191783Srmacklem	struct nfscllock *nlop, *otherlop;
905191783Srmacklem	struct nfscldeleg *dp = NULL, *ldp = NULL;
906191783Srmacklem	struct nfscllockownerhead *lhp = NULL;
907191783Srmacklem	struct nfsnode *np;
908223774Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp, openown[NFSV4CL_LOCKNAMELEN];
909223774Srmacklem	u_int8_t *openownp;
910191783Srmacklem	int error = 0, ret, donelocally = 0;
911191783Srmacklem	u_int32_t mode;
912191783Srmacklem
913223774Srmacklem	/* For Lock Ops, the open mode doesn't matter, so use 0 to match any. */
914223774Srmacklem	mode = 0;
915191783Srmacklem	np = VTONFS(vp);
916191783Srmacklem	*lpp = NULL;
917223774Srmacklem	lp = NULL;
918191783Srmacklem	*newonep = 0;
919191783Srmacklem	*donelocallyp = 0;
920191783Srmacklem
921191783Srmacklem	/*
922191783Srmacklem	 * Might need these, so MALLOC them now, to
923191783Srmacklem	 * avoid a tsleep() in MALLOC later.
924191783Srmacklem	 */
925191783Srmacklem	MALLOC(nlp, struct nfscllockowner *,
926191783Srmacklem	    sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK);
927191783Srmacklem	MALLOC(otherlop, struct nfscllock *,
928191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
929191783Srmacklem	MALLOC(nlop, struct nfscllock *,
930191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
931191783Srmacklem	nlop->nfslo_type = type;
932191783Srmacklem	nlop->nfslo_first = off;
933191783Srmacklem	if (len == NFS64BITSSET) {
934191783Srmacklem		nlop->nfslo_end = NFS64BITSSET;
935191783Srmacklem	} else {
936191783Srmacklem		nlop->nfslo_end = off + len;
937191783Srmacklem		if (nlop->nfslo_end <= nlop->nfslo_first)
938191783Srmacklem			error = NFSERR_INVAL;
939191783Srmacklem	}
940191783Srmacklem
941191783Srmacklem	if (!error) {
942191783Srmacklem		if (recovery)
943191783Srmacklem			clp = rclp;
944191783Srmacklem		else
945191783Srmacklem			error = nfscl_getcl(vp, cred, p, &clp);
946191783Srmacklem	}
947191783Srmacklem	if (error) {
948191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
949191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
950191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
951191783Srmacklem		return (error);
952191783Srmacklem	}
953191783Srmacklem
954191783Srmacklem	op = NULL;
955191783Srmacklem	if (recovery) {
956191783Srmacklem		ownp = rownp;
957223774Srmacklem		openownp = ropenownp;
958191783Srmacklem	} else {
959222719Srmacklem		nfscl_filllockowner(id, own, flags);
960191783Srmacklem		ownp = own;
961223774Srmacklem		nfscl_filllockowner(p->td_proc, openown, F_POSIX);
962223774Srmacklem		openownp = openown;
963191783Srmacklem	}
964191783Srmacklem	if (!recovery) {
965191783Srmacklem		NFSLOCKCLSTATE();
966191783Srmacklem		/*
967191783Srmacklem		 * First, search for a delegation. If one exists for this file,
968191783Srmacklem		 * the lock can be done locally against it, so long as there
969191783Srmacklem		 * isn't a local lock conflict.
970191783Srmacklem		 */
971191783Srmacklem		ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
972191783Srmacklem		    np->n_fhp->nfh_len);
973191783Srmacklem		/* Just sanity check for correct type of delegation */
974214406Srmacklem		if (dp != NULL && ((dp->nfsdl_flags &
975214406Srmacklem		    (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 ||
976214406Srmacklem		     (type == F_WRLCK &&
977214406Srmacklem		      (dp->nfsdl_flags & NFSCLDL_WRITE) == 0)))
978191783Srmacklem			dp = NULL;
979191783Srmacklem	}
980191783Srmacklem	if (dp != NULL) {
981223774Srmacklem		/* Now, find an open and maybe a lockowner. */
982191783Srmacklem		ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh,
983223774Srmacklem		    np->n_fhp->nfh_len, openownp, ownp, mode, NULL, &op);
984191783Srmacklem		if (ret)
985191783Srmacklem			ret = nfscl_getopen(&clp->nfsc_owner,
986223774Srmacklem			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
987223774Srmacklem			    ownp, mode, NULL, &op);
988191783Srmacklem		if (!ret) {
989191783Srmacklem			lhp = &dp->nfsdl_lock;
990191783Srmacklem			TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
991191783Srmacklem			TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
992191783Srmacklem			dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
993191783Srmacklem			donelocally = 1;
994191783Srmacklem		} else {
995191783Srmacklem			dp = NULL;
996191783Srmacklem		}
997191783Srmacklem	}
998191783Srmacklem	if (!donelocally) {
999191783Srmacklem		/*
1000223774Srmacklem		 * Get the related Open and maybe lockowner.
1001191783Srmacklem		 */
1002223774Srmacklem		error = nfscl_getopen(&clp->nfsc_owner,
1003223774Srmacklem		    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1004223774Srmacklem		    ownp, mode, &lp, &op);
1005191783Srmacklem		if (!error)
1006191783Srmacklem			lhp = &op->nfso_lock;
1007191783Srmacklem	}
1008191783Srmacklem	if (!error && !recovery)
1009201439Srmacklem		error = nfscl_localconflict(clp, np->n_fhp->nfh_fh,
1010201439Srmacklem		    np->n_fhp->nfh_len, nlop, ownp, ldp, NULL);
1011191783Srmacklem	if (error) {
1012191783Srmacklem		if (!recovery) {
1013191783Srmacklem			nfscl_clrelease(clp);
1014191783Srmacklem			NFSUNLOCKCLSTATE();
1015191783Srmacklem		}
1016191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1017191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1018191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
1019191783Srmacklem		return (error);
1020191783Srmacklem	}
1021191783Srmacklem
1022191783Srmacklem	/*
1023191783Srmacklem	 * Ok, see if a lockowner exists and create one, as required.
1024191783Srmacklem	 */
1025223774Srmacklem	if (lp == NULL)
1026223774Srmacklem		LIST_FOREACH(lp, lhp, nfsl_list) {
1027223774Srmacklem			if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
1028223774Srmacklem				break;
1029223774Srmacklem		}
1030191783Srmacklem	if (lp == NULL) {
1031191783Srmacklem		NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
1032191783Srmacklem		if (recovery)
1033191783Srmacklem			NFSBCOPY(ropenownp, nlp->nfsl_openowner,
1034191783Srmacklem			    NFSV4CL_LOCKNAMELEN);
1035191783Srmacklem		else
1036191783Srmacklem			NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner,
1037191783Srmacklem			    NFSV4CL_LOCKNAMELEN);
1038191783Srmacklem		nlp->nfsl_seqid = 0;
1039229752Srmacklem		nlp->nfsl_lockflags = flags;
1040191783Srmacklem		nlp->nfsl_inprog = NULL;
1041191783Srmacklem		nfscl_lockinit(&nlp->nfsl_rwlock);
1042191783Srmacklem		LIST_INIT(&nlp->nfsl_lock);
1043191783Srmacklem		if (donelocally) {
1044191783Srmacklem			nlp->nfsl_open = NULL;
1045191783Srmacklem			newnfsstats.cllocallockowners++;
1046191783Srmacklem		} else {
1047191783Srmacklem			nlp->nfsl_open = op;
1048191783Srmacklem			newnfsstats.cllockowners++;
1049191783Srmacklem		}
1050191783Srmacklem		LIST_INSERT_HEAD(lhp, nlp, nfsl_list);
1051191783Srmacklem		lp = nlp;
1052191783Srmacklem		nlp = NULL;
1053191783Srmacklem		*newonep = 1;
1054191783Srmacklem	}
1055191783Srmacklem
1056191783Srmacklem	/*
1057191783Srmacklem	 * Now, update the byte ranges for locks.
1058191783Srmacklem	 */
1059191783Srmacklem	ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally);
1060191783Srmacklem	if (!ret)
1061191783Srmacklem		donelocally = 1;
1062191783Srmacklem	if (donelocally) {
1063191783Srmacklem		*donelocallyp = 1;
1064191783Srmacklem		if (!recovery)
1065191783Srmacklem			nfscl_clrelease(clp);
1066191783Srmacklem	} else {
1067191783Srmacklem		/*
1068191783Srmacklem		 * Serial modifications on the lock owner for multiple threads
1069191783Srmacklem		 * for the same process using a read/write lock.
1070191783Srmacklem		 */
1071191783Srmacklem		if (!recovery)
1072191783Srmacklem			nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1073191783Srmacklem	}
1074191783Srmacklem	if (!recovery)
1075191783Srmacklem		NFSUNLOCKCLSTATE();
1076191783Srmacklem
1077191783Srmacklem	if (nlp)
1078191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1079191783Srmacklem	if (nlop)
1080191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
1081191783Srmacklem	if (otherlop)
1082191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1083191783Srmacklem
1084191783Srmacklem	*lpp = lp;
1085191783Srmacklem	return (0);
1086191783Srmacklem}
1087191783Srmacklem
1088191783Srmacklem/*
1089191783Srmacklem * Called to unlock a byte range, for LockU.
1090191783Srmacklem */
1091191783SrmacklemAPPLESTATIC int
1092191783Srmacklemnfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
1093191783Srmacklem    __unused struct ucred *cred, NFSPROC_T *p, int callcnt,
1094222719Srmacklem    struct nfsclclient *clp, void *id, int flags,
1095222719Srmacklem    struct nfscllockowner **lpp, int *dorpcp)
1096191783Srmacklem{
1097191783Srmacklem	struct nfscllockowner *lp;
1098191783Srmacklem	struct nfsclowner *owp;
1099191783Srmacklem	struct nfsclopen *op;
1100191783Srmacklem	struct nfscllock *nlop, *other_lop = NULL;
1101191783Srmacklem	struct nfscldeleg *dp;
1102191783Srmacklem	struct nfsnode *np;
1103191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1104201439Srmacklem	int ret = 0, fnd;
1105191783Srmacklem
1106191783Srmacklem	np = VTONFS(vp);
1107191783Srmacklem	*lpp = NULL;
1108191783Srmacklem	*dorpcp = 0;
1109191783Srmacklem
1110191783Srmacklem	/*
1111191783Srmacklem	 * Might need these, so MALLOC them now, to
1112191783Srmacklem	 * avoid a tsleep() in MALLOC later.
1113191783Srmacklem	 */
1114191783Srmacklem	MALLOC(nlop, struct nfscllock *,
1115191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1116191783Srmacklem	nlop->nfslo_type = F_UNLCK;
1117191783Srmacklem	nlop->nfslo_first = off;
1118191783Srmacklem	if (len == NFS64BITSSET) {
1119191783Srmacklem		nlop->nfslo_end = NFS64BITSSET;
1120191783Srmacklem	} else {
1121191783Srmacklem		nlop->nfslo_end = off + len;
1122191783Srmacklem		if (nlop->nfslo_end <= nlop->nfslo_first) {
1123191783Srmacklem			FREE((caddr_t)nlop, M_NFSCLLOCK);
1124191783Srmacklem			return (NFSERR_INVAL);
1125191783Srmacklem		}
1126191783Srmacklem	}
1127191783Srmacklem	if (callcnt == 0) {
1128191783Srmacklem		MALLOC(other_lop, struct nfscllock *,
1129191783Srmacklem		    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1130191783Srmacklem		*other_lop = *nlop;
1131191783Srmacklem	}
1132222719Srmacklem	nfscl_filllockowner(id, own, flags);
1133191783Srmacklem	dp = NULL;
1134191783Srmacklem	NFSLOCKCLSTATE();
1135191783Srmacklem	if (callcnt == 0)
1136191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1137191783Srmacklem		    np->n_fhp->nfh_len);
1138191783Srmacklem
1139191783Srmacklem	/*
1140191783Srmacklem	 * First, unlock any local regions on a delegation.
1141191783Srmacklem	 */
1142191783Srmacklem	if (dp != NULL) {
1143191783Srmacklem		/* Look for this lockowner. */
1144191783Srmacklem		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1145191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1146191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1147191783Srmacklem				break;
1148191783Srmacklem		}
1149191783Srmacklem		if (lp != NULL)
1150191783Srmacklem			/* Use other_lop, so nlop is still available */
1151191783Srmacklem			(void)nfscl_updatelock(lp, &other_lop, NULL, 1);
1152191783Srmacklem	}
1153191783Srmacklem
1154191783Srmacklem	/*
1155191783Srmacklem	 * Now, find a matching open/lockowner that hasn't already been done,
1156191783Srmacklem	 * as marked by nfsl_inprog.
1157191783Srmacklem	 */
1158191783Srmacklem	lp = NULL;
1159191783Srmacklem	fnd = 0;
1160191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1161191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1162191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1163191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1164191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1165191783Srmacklem			if (lp->nfsl_inprog == NULL &&
1166191783Srmacklem			    !NFSBCMP(lp->nfsl_owner, own,
1167191783Srmacklem			     NFSV4CL_LOCKNAMELEN)) {
1168191783Srmacklem				fnd = 1;
1169191783Srmacklem				break;
1170191783Srmacklem			}
1171191783Srmacklem		    }
1172191783Srmacklem		    if (fnd)
1173191783Srmacklem			break;
1174191783Srmacklem		}
1175191783Srmacklem	    }
1176191783Srmacklem	    if (fnd)
1177191783Srmacklem		break;
1178191783Srmacklem	}
1179191783Srmacklem
1180191783Srmacklem	if (lp != NULL) {
1181191783Srmacklem		ret = nfscl_updatelock(lp, &nlop, NULL, 0);
1182191783Srmacklem		if (ret)
1183191783Srmacklem			*dorpcp = 1;
1184191783Srmacklem		/*
1185191783Srmacklem		 * Serial modifications on the lock owner for multiple
1186191783Srmacklem		 * threads for the same process using a read/write lock.
1187191783Srmacklem		 */
1188191783Srmacklem		lp->nfsl_inprog = p;
1189191783Srmacklem		nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1190191783Srmacklem		*lpp = lp;
1191191783Srmacklem	}
1192191783Srmacklem	NFSUNLOCKCLSTATE();
1193191783Srmacklem	if (nlop)
1194191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
1195191783Srmacklem	if (other_lop)
1196191783Srmacklem		FREE((caddr_t)other_lop, M_NFSCLLOCK);
1197191783Srmacklem	return (0);
1198191783Srmacklem}
1199191783Srmacklem
1200191783Srmacklem/*
1201191783Srmacklem * Release all lockowners marked in progess for this process and file.
1202191783Srmacklem */
1203191783SrmacklemAPPLESTATIC void
1204222719Srmacklemnfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p,
1205222719Srmacklem    void *id, int flags)
1206191783Srmacklem{
1207191783Srmacklem	struct nfsclowner *owp;
1208191783Srmacklem	struct nfsclopen *op;
1209191783Srmacklem	struct nfscllockowner *lp;
1210191783Srmacklem	struct nfsnode *np;
1211191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1212191783Srmacklem
1213191783Srmacklem	np = VTONFS(vp);
1214222719Srmacklem	nfscl_filllockowner(id, own, flags);
1215191783Srmacklem	NFSLOCKCLSTATE();
1216191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1217191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1218191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1219191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1220191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1221191783Srmacklem			if (lp->nfsl_inprog == p &&
1222191783Srmacklem			    !NFSBCMP(lp->nfsl_owner, own,
1223191783Srmacklem			    NFSV4CL_LOCKNAMELEN)) {
1224191783Srmacklem			    lp->nfsl_inprog = NULL;
1225191783Srmacklem			    nfscl_lockunlock(&lp->nfsl_rwlock);
1226191783Srmacklem			}
1227191783Srmacklem		    }
1228191783Srmacklem		}
1229191783Srmacklem	    }
1230191783Srmacklem	}
1231191783Srmacklem	nfscl_clrelease(clp);
1232191783Srmacklem	NFSUNLOCKCLSTATE();
1233191783Srmacklem}
1234191783Srmacklem
1235191783Srmacklem/*
1236191783Srmacklem * Called to find out if any bytes within the byte range specified are
1237191783Srmacklem * write locked by the calling process. Used to determine if flushing
1238191783Srmacklem * is required before a LockU.
1239191783Srmacklem * If in doubt, return 1, so the flush will occur.
1240191783Srmacklem */
1241191783SrmacklemAPPLESTATIC int
1242191783Srmacklemnfscl_checkwritelocked(vnode_t vp, struct flock *fl,
1243222719Srmacklem    struct ucred *cred, NFSPROC_T *p, void *id, int flags)
1244191783Srmacklem{
1245191783Srmacklem	struct nfsclowner *owp;
1246191783Srmacklem	struct nfscllockowner *lp;
1247191783Srmacklem	struct nfsclopen *op;
1248191783Srmacklem	struct nfsclclient *clp;
1249191783Srmacklem	struct nfscllock *lop;
1250191783Srmacklem	struct nfscldeleg *dp;
1251191783Srmacklem	struct nfsnode *np;
1252191783Srmacklem	u_int64_t off, end;
1253191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1254191783Srmacklem	int error = 0;
1255191783Srmacklem
1256191783Srmacklem	np = VTONFS(vp);
1257191783Srmacklem	switch (fl->l_whence) {
1258191783Srmacklem	case SEEK_SET:
1259191783Srmacklem	case SEEK_CUR:
1260191783Srmacklem		/*
1261191783Srmacklem		 * Caller is responsible for adding any necessary offset
1262191783Srmacklem		 * when SEEK_CUR is used.
1263191783Srmacklem		 */
1264191783Srmacklem		off = fl->l_start;
1265191783Srmacklem		break;
1266191783Srmacklem	case SEEK_END:
1267191783Srmacklem		off = np->n_size + fl->l_start;
1268191783Srmacklem		break;
1269191783Srmacklem	default:
1270191783Srmacklem		return (1);
1271191783Srmacklem	};
1272191783Srmacklem	if (fl->l_len != 0) {
1273191783Srmacklem		end = off + fl->l_len;
1274191783Srmacklem		if (end < off)
1275191783Srmacklem			return (1);
1276191783Srmacklem	} else {
1277191783Srmacklem		end = NFS64BITSSET;
1278191783Srmacklem	}
1279191783Srmacklem
1280191783Srmacklem	error = nfscl_getcl(vp, cred, p, &clp);
1281191783Srmacklem	if (error)
1282191783Srmacklem		return (1);
1283222719Srmacklem	nfscl_filllockowner(id, own, flags);
1284191783Srmacklem	NFSLOCKCLSTATE();
1285191783Srmacklem
1286191783Srmacklem	/*
1287191783Srmacklem	 * First check the delegation locks.
1288191783Srmacklem	 */
1289191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
1290191783Srmacklem	if (dp != NULL) {
1291191783Srmacklem		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1292191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1293191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1294191783Srmacklem				break;
1295191783Srmacklem		}
1296191783Srmacklem		if (lp != NULL) {
1297191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1298191783Srmacklem				if (lop->nfslo_first >= end)
1299191783Srmacklem					break;
1300191783Srmacklem				if (lop->nfslo_end <= off)
1301191783Srmacklem					continue;
1302191783Srmacklem				if (lop->nfslo_type == F_WRLCK) {
1303191783Srmacklem					nfscl_clrelease(clp);
1304191783Srmacklem					NFSUNLOCKCLSTATE();
1305191783Srmacklem					return (1);
1306191783Srmacklem				}
1307191783Srmacklem			}
1308191783Srmacklem		}
1309191783Srmacklem	}
1310191783Srmacklem
1311191783Srmacklem	/*
1312191783Srmacklem	 * Now, check state against the server.
1313191783Srmacklem	 */
1314191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1315191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1316191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1317191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1318191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1319191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1320191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1321191783Srmacklem			    break;
1322191783Srmacklem		    }
1323191783Srmacklem		    if (lp != NULL) {
1324191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1325191783Srmacklem			    if (lop->nfslo_first >= end)
1326191783Srmacklem				break;
1327191783Srmacklem			    if (lop->nfslo_end <= off)
1328191783Srmacklem				continue;
1329191783Srmacklem			    if (lop->nfslo_type == F_WRLCK) {
1330191783Srmacklem				nfscl_clrelease(clp);
1331191783Srmacklem				NFSUNLOCKCLSTATE();
1332191783Srmacklem				return (1);
1333191783Srmacklem			    }
1334191783Srmacklem			}
1335191783Srmacklem		    }
1336191783Srmacklem		}
1337191783Srmacklem	    }
1338191783Srmacklem	}
1339191783Srmacklem	nfscl_clrelease(clp);
1340191783Srmacklem	NFSUNLOCKCLSTATE();
1341191783Srmacklem	return (0);
1342191783Srmacklem}
1343191783Srmacklem
1344191783Srmacklem/*
1345191783Srmacklem * Release a byte range lock owner structure.
1346191783Srmacklem */
1347191783SrmacklemAPPLESTATIC void
1348191783Srmacklemnfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete)
1349191783Srmacklem{
1350191783Srmacklem	struct nfsclclient *clp;
1351191783Srmacklem
1352191783Srmacklem	if (lp == NULL)
1353191783Srmacklem		return;
1354191783Srmacklem	NFSLOCKCLSTATE();
1355191783Srmacklem	clp = lp->nfsl_open->nfso_own->nfsow_clp;
1356191783Srmacklem	if (error != 0 && candelete &&
1357191783Srmacklem	    (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0)
1358191783Srmacklem		nfscl_freelockowner(lp, 0);
1359191783Srmacklem	else
1360191783Srmacklem		nfscl_lockunlock(&lp->nfsl_rwlock);
1361191783Srmacklem	nfscl_clrelease(clp);
1362191783Srmacklem	NFSUNLOCKCLSTATE();
1363191783Srmacklem}
1364191783Srmacklem
1365191783Srmacklem/*
1366191783Srmacklem * Free up an open structure and any associated byte range lock structures.
1367191783Srmacklem */
1368191783SrmacklemAPPLESTATIC void
1369191783Srmacklemnfscl_freeopen(struct nfsclopen *op, int local)
1370191783Srmacklem{
1371191783Srmacklem
1372191783Srmacklem	LIST_REMOVE(op, nfso_list);
1373191783Srmacklem	nfscl_freealllocks(&op->nfso_lock, local);
1374191783Srmacklem	FREE((caddr_t)op, M_NFSCLOPEN);
1375191783Srmacklem	if (local)
1376191783Srmacklem		newnfsstats.cllocalopens--;
1377191783Srmacklem	else
1378191783Srmacklem		newnfsstats.clopens--;
1379191783Srmacklem}
1380191783Srmacklem
1381191783Srmacklem/*
1382191783Srmacklem * Free up all lock owners and associated locks.
1383191783Srmacklem */
1384191783Srmacklemstatic void
1385191783Srmacklemnfscl_freealllocks(struct nfscllockownerhead *lhp, int local)
1386191783Srmacklem{
1387191783Srmacklem	struct nfscllockowner *lp, *nlp;
1388191783Srmacklem
1389191783Srmacklem	LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) {
1390191783Srmacklem		if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1391191783Srmacklem			panic("nfscllckw");
1392191783Srmacklem		nfscl_freelockowner(lp, local);
1393191783Srmacklem	}
1394191783Srmacklem}
1395191783Srmacklem
1396191783Srmacklem/*
1397191783Srmacklem * Called for an Open when NFSERR_EXPIRED is received from the server.
1398191783Srmacklem * If there are no byte range locks nor a Share Deny lost, try to do a
1399191783Srmacklem * fresh Open. Otherwise, free the open.
1400191783Srmacklem */
1401191783Srmacklemstatic int
1402191783Srmacklemnfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op,
1403191783Srmacklem    struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
1404191783Srmacklem{
1405191783Srmacklem	struct nfscllockowner *lp;
1406191783Srmacklem	struct nfscldeleg *dp;
1407191783Srmacklem	int mustdelete = 0, error;
1408191783Srmacklem
1409191783Srmacklem	/*
1410191783Srmacklem	 * Look for any byte range lock(s).
1411191783Srmacklem	 */
1412191783Srmacklem	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1413191783Srmacklem		if (!LIST_EMPTY(&lp->nfsl_lock)) {
1414191783Srmacklem			mustdelete = 1;
1415191783Srmacklem			break;
1416191783Srmacklem		}
1417191783Srmacklem	}
1418191783Srmacklem
1419191783Srmacklem	/*
1420191783Srmacklem	 * If no byte range lock(s) nor a Share deny, try to re-open.
1421191783Srmacklem	 */
1422191783Srmacklem	if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) {
1423191783Srmacklem		newnfs_copycred(&op->nfso_cred, cred);
1424191783Srmacklem		dp = NULL;
1425191783Srmacklem		error = nfsrpc_reopen(nmp, op->nfso_fh,
1426191783Srmacklem		    op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p);
1427191783Srmacklem		if (error) {
1428191783Srmacklem			mustdelete = 1;
1429191783Srmacklem			if (dp != NULL) {
1430191783Srmacklem				FREE((caddr_t)dp, M_NFSCLDELEG);
1431191783Srmacklem				dp = NULL;
1432191783Srmacklem			}
1433191783Srmacklem		}
1434191783Srmacklem		if (dp != NULL)
1435191783Srmacklem			nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh,
1436191783Srmacklem			    op->nfso_fhlen, cred, p, &dp);
1437191783Srmacklem	}
1438191783Srmacklem
1439191783Srmacklem	/*
1440191783Srmacklem	 * If a byte range lock or Share deny or couldn't re-open, free it.
1441191783Srmacklem	 */
1442191783Srmacklem	if (mustdelete)
1443191783Srmacklem		nfscl_freeopen(op, 0);
1444191783Srmacklem	return (mustdelete);
1445191783Srmacklem}
1446191783Srmacklem
1447191783Srmacklem/*
1448191783Srmacklem * Free up an open owner structure.
1449191783Srmacklem */
1450191783Srmacklemstatic void
1451191783Srmacklemnfscl_freeopenowner(struct nfsclowner *owp, int local)
1452191783Srmacklem{
1453191783Srmacklem
1454191783Srmacklem	LIST_REMOVE(owp, nfsow_list);
1455191783Srmacklem	FREE((caddr_t)owp, M_NFSCLOWNER);
1456191783Srmacklem	if (local)
1457191783Srmacklem		newnfsstats.cllocalopenowners--;
1458191783Srmacklem	else
1459191783Srmacklem		newnfsstats.clopenowners--;
1460191783Srmacklem}
1461191783Srmacklem
1462191783Srmacklem/*
1463191783Srmacklem * Free up a byte range lock owner structure.
1464191783Srmacklem */
1465223747SrmacklemAPPLESTATIC void
1466191783Srmacklemnfscl_freelockowner(struct nfscllockowner *lp, int local)
1467191783Srmacklem{
1468191783Srmacklem	struct nfscllock *lop, *nlop;
1469191783Srmacklem
1470191783Srmacklem	LIST_REMOVE(lp, nfsl_list);
1471191783Srmacklem	LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
1472191783Srmacklem		nfscl_freelock(lop, local);
1473191783Srmacklem	}
1474191783Srmacklem	FREE((caddr_t)lp, M_NFSCLLOCKOWNER);
1475191783Srmacklem	if (local)
1476191783Srmacklem		newnfsstats.cllocallockowners--;
1477191783Srmacklem	else
1478191783Srmacklem		newnfsstats.cllockowners--;
1479191783Srmacklem}
1480191783Srmacklem
1481191783Srmacklem/*
1482191783Srmacklem * Free up a byte range lock structure.
1483191783Srmacklem */
1484191783SrmacklemAPPLESTATIC void
1485191783Srmacklemnfscl_freelock(struct nfscllock *lop, int local)
1486191783Srmacklem{
1487191783Srmacklem
1488191783Srmacklem	LIST_REMOVE(lop, nfslo_list);
1489191783Srmacklem	FREE((caddr_t)lop, M_NFSCLLOCK);
1490191783Srmacklem	if (local)
1491191783Srmacklem		newnfsstats.cllocallocks--;
1492191783Srmacklem	else
1493191783Srmacklem		newnfsstats.cllocks--;
1494191783Srmacklem}
1495191783Srmacklem
1496191783Srmacklem/*
1497191783Srmacklem * Clean out the state related to a delegation.
1498191783Srmacklem */
1499191783Srmacklemstatic void
1500191783Srmacklemnfscl_cleandeleg(struct nfscldeleg *dp)
1501191783Srmacklem{
1502191783Srmacklem	struct nfsclowner *owp, *nowp;
1503191783Srmacklem	struct nfsclopen *op;
1504191783Srmacklem
1505191783Srmacklem	LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
1506191783Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
1507191783Srmacklem		if (op != NULL) {
1508191783Srmacklem			if (LIST_NEXT(op, nfso_list) != NULL)
1509191783Srmacklem				panic("nfscleandel");
1510191783Srmacklem			nfscl_freeopen(op, 1);
1511191783Srmacklem		}
1512191783Srmacklem		nfscl_freeopenowner(owp, 1);
1513191783Srmacklem	}
1514191783Srmacklem	nfscl_freealllocks(&dp->nfsdl_lock, 1);
1515191783Srmacklem}
1516191783Srmacklem
1517191783Srmacklem/*
1518191783Srmacklem * Free a delegation.
1519191783Srmacklem */
1520191783Srmacklemstatic void
1521191783Srmacklemnfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp)
1522191783Srmacklem{
1523191783Srmacklem
1524191783Srmacklem	TAILQ_REMOVE(hdp, dp, nfsdl_list);
1525191783Srmacklem	LIST_REMOVE(dp, nfsdl_hash);
1526191783Srmacklem	FREE((caddr_t)dp, M_NFSCLDELEG);
1527191783Srmacklem	newnfsstats.cldelegates--;
1528191783Srmacklem	nfscl_delegcnt--;
1529191783Srmacklem}
1530191783Srmacklem
1531191783Srmacklem/*
1532191783Srmacklem * Free up all state related to this client structure.
1533191783Srmacklem */
1534191783Srmacklemstatic void
1535191783Srmacklemnfscl_cleanclient(struct nfsclclient *clp)
1536191783Srmacklem{
1537191783Srmacklem	struct nfsclowner *owp, *nowp;
1538191783Srmacklem	struct nfsclopen *op, *nop;
1539191783Srmacklem
1540191783Srmacklem	/* Now, all the OpenOwners, etc. */
1541191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1542191783Srmacklem		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1543191783Srmacklem			nfscl_freeopen(op, 0);
1544191783Srmacklem		}
1545191783Srmacklem		nfscl_freeopenowner(owp, 0);
1546191783Srmacklem	}
1547191783Srmacklem}
1548191783Srmacklem
1549191783Srmacklem/*
1550191783Srmacklem * Called when an NFSERR_EXPIRED is received from the server.
1551191783Srmacklem */
1552191783Srmacklemstatic void
1553191783Srmacklemnfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
1554191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
1555191783Srmacklem{
1556191783Srmacklem	struct nfsclowner *owp, *nowp, *towp;
1557191783Srmacklem	struct nfsclopen *op, *nop, *top;
1558191783Srmacklem	struct nfscldeleg *dp, *ndp;
1559191783Srmacklem	int ret, printed = 0;
1560191783Srmacklem
1561191783Srmacklem	/*
1562191783Srmacklem	 * First, merge locally issued Opens into the list for the server.
1563191783Srmacklem	 */
1564191783Srmacklem	dp = TAILQ_FIRST(&clp->nfsc_deleg);
1565191783Srmacklem	while (dp != NULL) {
1566191783Srmacklem	    ndp = TAILQ_NEXT(dp, nfsdl_list);
1567191783Srmacklem	    owp = LIST_FIRST(&dp->nfsdl_owner);
1568191783Srmacklem	    while (owp != NULL) {
1569191783Srmacklem		nowp = LIST_NEXT(owp, nfsow_list);
1570191783Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
1571191783Srmacklem		if (op != NULL) {
1572191783Srmacklem		    if (LIST_NEXT(op, nfso_list) != NULL)
1573191783Srmacklem			panic("nfsclexp");
1574191783Srmacklem		    LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) {
1575191783Srmacklem			if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner,
1576191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1577191783Srmacklem			    break;
1578191783Srmacklem		    }
1579191783Srmacklem		    if (towp != NULL) {
1580191783Srmacklem			/* Merge opens in */
1581191783Srmacklem			LIST_FOREACH(top, &towp->nfsow_open, nfso_list) {
1582191783Srmacklem			    if (top->nfso_fhlen == op->nfso_fhlen &&
1583191783Srmacklem				!NFSBCMP(top->nfso_fh, op->nfso_fh,
1584191783Srmacklem				 op->nfso_fhlen)) {
1585191783Srmacklem				top->nfso_mode |= op->nfso_mode;
1586191783Srmacklem				top->nfso_opencnt += op->nfso_opencnt;
1587191783Srmacklem				break;
1588191783Srmacklem			    }
1589191783Srmacklem			}
1590191783Srmacklem			if (top == NULL) {
1591191783Srmacklem			    /* Just add the open to the owner list */
1592191783Srmacklem			    LIST_REMOVE(op, nfso_list);
1593191783Srmacklem			    op->nfso_own = towp;
1594191783Srmacklem			    LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list);
1595191783Srmacklem			    newnfsstats.cllocalopens--;
1596191783Srmacklem			    newnfsstats.clopens++;
1597191783Srmacklem			}
1598191783Srmacklem		    } else {
1599191783Srmacklem			/* Just add the openowner to the client list */
1600191783Srmacklem			LIST_REMOVE(owp, nfsow_list);
1601191783Srmacklem			owp->nfsow_clp = clp;
1602191783Srmacklem			LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list);
1603191783Srmacklem			newnfsstats.cllocalopenowners--;
1604191783Srmacklem			newnfsstats.clopenowners++;
1605191783Srmacklem			newnfsstats.cllocalopens--;
1606191783Srmacklem			newnfsstats.clopens++;
1607191783Srmacklem		    }
1608191783Srmacklem		}
1609191783Srmacklem		owp = nowp;
1610191783Srmacklem	    }
1611191783Srmacklem	    if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) {
1612191783Srmacklem		printed = 1;
1613191783Srmacklem		printf("nfsv4 expired locks lost\n");
1614191783Srmacklem	    }
1615191783Srmacklem	    nfscl_cleandeleg(dp);
1616191783Srmacklem	    nfscl_freedeleg(&clp->nfsc_deleg, dp);
1617191783Srmacklem	    dp = ndp;
1618191783Srmacklem	}
1619191783Srmacklem	if (!TAILQ_EMPTY(&clp->nfsc_deleg))
1620191783Srmacklem	    panic("nfsclexp");
1621191783Srmacklem
1622191783Srmacklem	/*
1623191783Srmacklem	 * Now, try and reopen against the server.
1624191783Srmacklem	 */
1625191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1626191783Srmacklem		owp->nfsow_seqid = 0;
1627191783Srmacklem		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1628191783Srmacklem			ret = nfscl_expireopen(clp, op, nmp, cred, p);
1629191783Srmacklem			if (ret && !printed) {
1630191783Srmacklem				printed = 1;
1631191783Srmacklem				printf("nfsv4 expired locks lost\n");
1632191783Srmacklem			}
1633191783Srmacklem		}
1634191783Srmacklem		if (LIST_EMPTY(&owp->nfsow_open))
1635191783Srmacklem			nfscl_freeopenowner(owp, 0);
1636191783Srmacklem	}
1637191783Srmacklem}
1638191783Srmacklem
1639191783Srmacklem/*
1640229599Srmacklem * This function must be called after the process represented by "own" has
1641229599Srmacklem * exited. Must be called with CLSTATE lock held.
1642191783Srmacklem */
1643191783Srmacklemstatic void
1644191783Srmacklemnfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own)
1645191783Srmacklem{
1646191783Srmacklem	struct nfsclowner *owp, *nowp;
1647191783Srmacklem	struct nfscllockowner *lp, *nlp;
1648191783Srmacklem	struct nfscldeleg *dp;
1649191783Srmacklem
1650191783Srmacklem	/* First, get rid of local locks on delegations. */
1651191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1652191783Srmacklem		LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) {
1653191783Srmacklem		    if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
1654191783Srmacklem			if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1655191783Srmacklem			    panic("nfscllckw");
1656191783Srmacklem			nfscl_freelockowner(lp, 1);
1657191783Srmacklem		    }
1658191783Srmacklem		}
1659191783Srmacklem	}
1660191783Srmacklem	owp = LIST_FIRST(&clp->nfsc_owner);
1661191783Srmacklem	while (owp != NULL) {
1662191783Srmacklem		nowp = LIST_NEXT(owp, nfsow_list);
1663191783Srmacklem		if (!NFSBCMP(owp->nfsow_owner, own,
1664191783Srmacklem		    NFSV4CL_LOCKNAMELEN)) {
1665191783Srmacklem			/*
1666191783Srmacklem			 * If there are children that haven't closed the
1667191783Srmacklem			 * file descriptors yet, the opens will still be
1668191783Srmacklem			 * here. For that case, let the renew thread clear
1669191783Srmacklem			 * out the OpenOwner later.
1670191783Srmacklem			 */
1671191783Srmacklem			if (LIST_EMPTY(&owp->nfsow_open))
1672191783Srmacklem				nfscl_freeopenowner(owp, 0);
1673191783Srmacklem			else
1674191783Srmacklem				owp->nfsow_defunct = 1;
1675191783Srmacklem		}
1676191783Srmacklem		owp = nowp;
1677191783Srmacklem	}
1678191783Srmacklem}
1679191783Srmacklem
1680191783Srmacklem/*
1681229599Srmacklem * Find open/lock owners for processes that have exited.
1682191783Srmacklem */
1683191783Srmacklemstatic void
1684229752Srmacklemnfscl_cleanupkext(struct nfsclclient *clp, struct nfscllockownerfhhead *lhp)
1685191783Srmacklem{
1686191783Srmacklem	struct nfsclowner *owp, *nowp;
1687229752Srmacklem	struct nfsclopen *op;
1688229752Srmacklem	struct nfscllockowner *lp, *nlp;
1689191783Srmacklem
1690191783Srmacklem	NFSPROCLISTLOCK();
1691191783Srmacklem	NFSLOCKCLSTATE();
1692191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1693229752Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1694229752Srmacklem			LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp) {
1695229752Srmacklem				if (LIST_EMPTY(&lp->nfsl_lock))
1696229752Srmacklem					nfscl_emptylockowner(lp, lhp);
1697229752Srmacklem			}
1698229752Srmacklem		}
1699191783Srmacklem		if (nfscl_procdoesntexist(owp->nfsow_owner))
1700191783Srmacklem			nfscl_cleanup_common(clp, owp->nfsow_owner);
1701191783Srmacklem	}
1702191783Srmacklem	NFSUNLOCKCLSTATE();
1703191783Srmacklem	NFSPROCLISTUNLOCK();
1704191783Srmacklem}
1705191783Srmacklem
1706229752Srmacklem/*
1707229752Srmacklem * Take the empty lock owner and move it to the local lhp list if the
1708229752Srmacklem * associated process no longer exists.
1709229752Srmacklem */
1710229752Srmacklemstatic void
1711229752Srmacklemnfscl_emptylockowner(struct nfscllockowner *lp,
1712229752Srmacklem    struct nfscllockownerfhhead *lhp)
1713229752Srmacklem{
1714229752Srmacklem	struct nfscllockownerfh *lfhp, *mylfhp;
1715229752Srmacklem	struct nfscllockowner *nlp;
1716229752Srmacklem	int fnd_it;
1717229752Srmacklem
1718229752Srmacklem	/* If not a Posix lock owner, just return. */
1719229752Srmacklem	if ((lp->nfsl_lockflags & F_POSIX) == 0)
1720229752Srmacklem		return;
1721229752Srmacklem
1722229752Srmacklem	fnd_it = 0;
1723229752Srmacklem	mylfhp = NULL;
1724229752Srmacklem	/*
1725229752Srmacklem	 * First, search to see if this lock owner is already in the list.
1726229752Srmacklem	 * If it is, then the associated process no longer exists.
1727229752Srmacklem	 */
1728229752Srmacklem	SLIST_FOREACH(lfhp, lhp, nfslfh_list) {
1729229752Srmacklem		if (lfhp->nfslfh_len == lp->nfsl_open->nfso_fhlen &&
1730229752Srmacklem		    !NFSBCMP(lfhp->nfslfh_fh, lp->nfsl_open->nfso_fh,
1731229752Srmacklem		    lfhp->nfslfh_len))
1732229752Srmacklem			mylfhp = lfhp;
1733229752Srmacklem		LIST_FOREACH(nlp, &lfhp->nfslfh_lock, nfsl_list)
1734229752Srmacklem			if (!NFSBCMP(nlp->nfsl_owner, lp->nfsl_owner,
1735229752Srmacklem			    NFSV4CL_LOCKNAMELEN))
1736229752Srmacklem				fnd_it = 1;
1737229752Srmacklem	}
1738229752Srmacklem	/* If not found, check if process still exists. */
1739229752Srmacklem	if (fnd_it == 0 && nfscl_procdoesntexist(lp->nfsl_owner) == 0)
1740229752Srmacklem		return;
1741229752Srmacklem
1742229752Srmacklem	/* Move the lock owner over to the local list. */
1743229752Srmacklem	if (mylfhp == NULL) {
1744229752Srmacklem		mylfhp = malloc(sizeof(struct nfscllockownerfh), M_TEMP,
1745229752Srmacklem		    M_NOWAIT);
1746229752Srmacklem		if (mylfhp == NULL)
1747229752Srmacklem			return;
1748229752Srmacklem		mylfhp->nfslfh_len = lp->nfsl_open->nfso_fhlen;
1749229752Srmacklem		NFSBCOPY(lp->nfsl_open->nfso_fh, mylfhp->nfslfh_fh,
1750229752Srmacklem		    mylfhp->nfslfh_len);
1751229752Srmacklem		LIST_INIT(&mylfhp->nfslfh_lock);
1752229752Srmacklem		SLIST_INSERT_HEAD(lhp, mylfhp, nfslfh_list);
1753229752Srmacklem	}
1754229752Srmacklem	LIST_REMOVE(lp, nfsl_list);
1755229752Srmacklem	LIST_INSERT_HEAD(&mylfhp->nfslfh_lock, lp, nfsl_list);
1756229752Srmacklem}
1757229752Srmacklem
1758222389Srmacklemstatic int	fake_global;	/* Used to force visibility of MNTK_UNMOUNTF */
1759191783Srmacklem/*
1760191783Srmacklem * Called from nfs umount to free up the clientid.
1761191783Srmacklem */
1762191783SrmacklemAPPLESTATIC void
1763191783Srmacklemnfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
1764191783Srmacklem{
1765191783Srmacklem	struct nfsclclient *clp;
1766191783Srmacklem	struct ucred *cred;
1767191783Srmacklem	int igotlock;
1768191783Srmacklem
1769222389Srmacklem	/*
1770222389Srmacklem	 * For the case that matters, this is the thread that set
1771222389Srmacklem	 * MNTK_UNMOUNTF, so it will see it set. The code that follows is
1772222389Srmacklem	 * done to ensure that any thread executing nfscl_getcl() after
1773222389Srmacklem	 * this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the
1774222389Srmacklem	 * mutex for NFSLOCKCLSTATE(), so it is "m" for the following
1775222389Srmacklem	 * explanation, courtesy of Alan Cox.
1776222389Srmacklem	 * What follows is a snippet from Alan Cox's email at:
1777222389Srmacklem	 * http://docs.FreeBSD.org/cgi/
1778222389Srmacklem	 *     mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw
1779222389Srmacklem	 *
1780222389Srmacklem	 * 1. Set MNTK_UNMOUNTF
1781222389Srmacklem	 * 2. Acquire a standard FreeBSD mutex "m".
1782222389Srmacklem	 * 3. Update some data structures.
1783222389Srmacklem	 * 4. Release mutex "m".
1784222389Srmacklem	 *
1785222389Srmacklem	 * Then, other threads that acquire "m" after step 4 has occurred will
1786222389Srmacklem	 * see MNTK_UNMOUNTF as set.  But, other threads that beat thread X to
1787222389Srmacklem	 * step 2 may or may not see MNTK_UNMOUNTF as set.
1788222389Srmacklem	 */
1789222389Srmacklem	NFSLOCKCLSTATE();
1790222389Srmacklem	if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1791222389Srmacklem		fake_global++;
1792222389Srmacklem		NFSUNLOCKCLSTATE();
1793222389Srmacklem		NFSLOCKCLSTATE();
1794222389Srmacklem	}
1795222389Srmacklem
1796191783Srmacklem	clp = nmp->nm_clp;
1797191783Srmacklem	if (clp != NULL) {
1798191783Srmacklem		if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
1799191783Srmacklem			panic("nfscl umount");
1800191783Srmacklem
1801191783Srmacklem		/*
1802191783Srmacklem		 * First, handshake with the nfscl renew thread, to terminate
1803191783Srmacklem		 * it.
1804191783Srmacklem		 */
1805191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
1806191783Srmacklem		while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
1807222389Srmacklem			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT,
1808222389Srmacklem			    "nfsclumnt", hz);
1809191783Srmacklem
1810222389Srmacklem		/*
1811222389Srmacklem		 * Now, get the exclusive lock on the client state, so
1812222389Srmacklem		 * that no uses of the state are still in progress.
1813222389Srmacklem		 */
1814191783Srmacklem		do {
1815191783Srmacklem			igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1816222389Srmacklem			    NFSCLSTATEMUTEXPTR, NULL);
1817191783Srmacklem		} while (!igotlock);
1818191783Srmacklem		NFSUNLOCKCLSTATE();
1819191783Srmacklem
1820191783Srmacklem		/*
1821191783Srmacklem		 * Free up all the state. It will expire on the server, but
1822191783Srmacklem		 * maybe we should do a SetClientId/SetClientIdConfirm so
1823191783Srmacklem		 * the server throws it away?
1824191783Srmacklem		 */
1825191783Srmacklem		LIST_REMOVE(clp, nfsc_list);
1826191783Srmacklem		nfscl_delegreturnall(clp, p);
1827191783Srmacklem		cred = newnfs_getcred();
1828191783Srmacklem		(void) nfsrpc_setclient(nmp, clp, cred, p);
1829191783Srmacklem		nfscl_cleanclient(clp);
1830191783Srmacklem		nmp->nm_clp = NULL;
1831191783Srmacklem		NFSFREECRED(cred);
1832191783Srmacklem		FREE((caddr_t)clp, M_NFSCLCLIENT);
1833222389Srmacklem	} else
1834222389Srmacklem		NFSUNLOCKCLSTATE();
1835191783Srmacklem}
1836191783Srmacklem
1837191783Srmacklem/*
1838191783Srmacklem * This function is called when a server replies with NFSERR_STALECLIENTID
1839191783Srmacklem * or NFSERR_STALESTATEID. It traverses the clientid lists, doing Opens
1840191783Srmacklem * and Locks with reclaim. If these fail, it deletes the corresponding state.
1841191783Srmacklem */
1842191783Srmacklemstatic void
1843191783Srmacklemnfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
1844191783Srmacklem{
1845191783Srmacklem	struct nfsclowner *owp, *nowp;
1846191783Srmacklem	struct nfsclopen *op, *nop;
1847191783Srmacklem	struct nfscllockowner *lp, *nlp;
1848191783Srmacklem	struct nfscllock *lop, *nlop;
1849191783Srmacklem	struct nfscldeleg *dp, *ndp, *tdp;
1850191783Srmacklem	struct nfsmount *nmp;
1851191783Srmacklem	struct ucred *tcred;
1852191783Srmacklem	struct nfsclopenhead extra_open;
1853191783Srmacklem	struct nfscldeleghead extra_deleg;
1854191783Srmacklem	struct nfsreq *rep;
1855191783Srmacklem	u_int64_t len;
1856191783Srmacklem	u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
1857191783Srmacklem	int igotlock = 0, error, trycnt, firstlock, s;
1858191783Srmacklem
1859191783Srmacklem	/*
1860191783Srmacklem	 * First, lock the client structure, so everyone else will
1861191783Srmacklem	 * block when trying to use state.
1862191783Srmacklem	 */
1863191783Srmacklem	NFSLOCKCLSTATE();
1864206818Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
1865191783Srmacklem	do {
1866191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1867222389Srmacklem		    NFSCLSTATEMUTEXPTR, NULL);
1868191783Srmacklem	} while (!igotlock);
1869191783Srmacklem	NFSUNLOCKCLSTATE();
1870191783Srmacklem
1871191783Srmacklem	nmp = clp->nfsc_nmp;
1872191783Srmacklem	if (nmp == NULL)
1873191783Srmacklem		panic("nfscl recover");
1874191783Srmacklem	trycnt = 5;
1875191783Srmacklem	do {
1876191783Srmacklem		error = nfsrpc_setclient(nmp, clp, cred, p);
1877191783Srmacklem	} while ((error == NFSERR_STALECLIENTID ||
1878191783Srmacklem	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
1879191783Srmacklem	if (error) {
1880191783Srmacklem		nfscl_cleanclient(clp);
1881206818Srmacklem		NFSLOCKCLSTATE();
1882191783Srmacklem		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
1883206818Srmacklem		    NFSCLFLAGS_RECOVER | NFSCLFLAGS_RECVRINPROG);
1884206818Srmacklem		wakeup(&clp->nfsc_flags);
1885191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
1886191783Srmacklem		NFSUNLOCKCLSTATE();
1887191783Srmacklem		return;
1888191783Srmacklem	}
1889191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
1890191783Srmacklem	clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
1891191783Srmacklem
1892191783Srmacklem	/*
1893191783Srmacklem	 * Mark requests already queued on the server, so that they don't
1894191783Srmacklem	 * initiate another recovery cycle. Any requests already in the
1895191783Srmacklem	 * queue that handle state information will have the old stale
1896191783Srmacklem	 * clientid/stateid and will get a NFSERR_STALESTATEID or
1897191783Srmacklem	 * NFSERR_STALECLIENTID reply from the server. This will be
1898191783Srmacklem	 * translated to NFSERR_STALEDONTRECOVER when R_DONTRECOVER is set.
1899191783Srmacklem	 */
1900191783Srmacklem	s = splsoftclock();
1901191783Srmacklem	NFSLOCKREQ();
1902191783Srmacklem	TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) {
1903191783Srmacklem		if (rep->r_nmp == nmp)
1904191783Srmacklem			rep->r_flags |= R_DONTRECOVER;
1905191783Srmacklem	}
1906191783Srmacklem	NFSUNLOCKREQ();
1907191783Srmacklem	splx(s);
1908191783Srmacklem
1909191783Srmacklem	/*
1910191783Srmacklem	 * Now, mark all delegations "need reclaim".
1911191783Srmacklem	 */
1912191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
1913191783Srmacklem		dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM;
1914191783Srmacklem
1915191783Srmacklem	TAILQ_INIT(&extra_deleg);
1916191783Srmacklem	LIST_INIT(&extra_open);
1917191783Srmacklem	/*
1918191783Srmacklem	 * Now traverse the state lists, doing Open and Lock Reclaims.
1919191783Srmacklem	 */
1920191783Srmacklem	tcred = newnfs_getcred();
1921191783Srmacklem	owp = LIST_FIRST(&clp->nfsc_owner);
1922191783Srmacklem	while (owp != NULL) {
1923191783Srmacklem	    nowp = LIST_NEXT(owp, nfsow_list);
1924191783Srmacklem	    owp->nfsow_seqid = 0;
1925191783Srmacklem	    op = LIST_FIRST(&owp->nfsow_open);
1926191783Srmacklem	    while (op != NULL) {
1927191783Srmacklem		nop = LIST_NEXT(op, nfso_list);
1928191783Srmacklem		if (error != NFSERR_NOGRACE) {
1929191783Srmacklem		    /* Search for a delegation to reclaim with the open */
1930191783Srmacklem		    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1931191783Srmacklem			if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1932191783Srmacklem			    continue;
1933191783Srmacklem			if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
1934191783Srmacklem			    mode = NFSV4OPEN_ACCESSWRITE;
1935191783Srmacklem			    delegtype = NFSV4OPEN_DELEGATEWRITE;
1936191783Srmacklem			} else {
1937191783Srmacklem			    mode = NFSV4OPEN_ACCESSREAD;
1938191783Srmacklem			    delegtype = NFSV4OPEN_DELEGATEREAD;
1939191783Srmacklem			}
1940191783Srmacklem			if ((op->nfso_mode & mode) == mode &&
1941191783Srmacklem			    op->nfso_fhlen == dp->nfsdl_fhlen &&
1942191783Srmacklem			    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen))
1943191783Srmacklem			    break;
1944191783Srmacklem		    }
1945191783Srmacklem		    ndp = dp;
1946191783Srmacklem		    if (dp == NULL)
1947191783Srmacklem			delegtype = NFSV4OPEN_DELEGATENONE;
1948191783Srmacklem		    newnfs_copycred(&op->nfso_cred, tcred);
1949191783Srmacklem		    error = nfscl_tryopen(nmp, NULL, op->nfso_fh,
1950191783Srmacklem			op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen,
1951191783Srmacklem			op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
1952191783Srmacklem			tcred, p);
1953191783Srmacklem		    if (!error) {
1954191783Srmacklem			/* Handle any replied delegation */
1955191783Srmacklem			if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
1956191783Srmacklem			    || NFSMNT_RDONLY(nmp->nm_mountp))) {
1957191783Srmacklem			    if ((ndp->nfsdl_flags & NFSCLDL_WRITE))
1958191783Srmacklem				mode = NFSV4OPEN_ACCESSWRITE;
1959191783Srmacklem			    else
1960191783Srmacklem				mode = NFSV4OPEN_ACCESSREAD;
1961191783Srmacklem			    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1962191783Srmacklem				if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1963191783Srmacklem				    continue;
1964191783Srmacklem				if ((op->nfso_mode & mode) == mode &&
1965191783Srmacklem				    op->nfso_fhlen == dp->nfsdl_fhlen &&
1966191783Srmacklem				    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh,
1967191783Srmacklem				    op->nfso_fhlen)) {
1968191783Srmacklem				    dp->nfsdl_stateid = ndp->nfsdl_stateid;
1969191783Srmacklem				    dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit;
1970191783Srmacklem				    dp->nfsdl_ace = ndp->nfsdl_ace;
1971191783Srmacklem				    dp->nfsdl_change = ndp->nfsdl_change;
1972191783Srmacklem				    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
1973191783Srmacklem				    if ((ndp->nfsdl_flags & NFSCLDL_RECALL))
1974191783Srmacklem					dp->nfsdl_flags |= NFSCLDL_RECALL;
1975191783Srmacklem				    FREE((caddr_t)ndp, M_NFSCLDELEG);
1976191783Srmacklem				    ndp = NULL;
1977191783Srmacklem				    break;
1978191783Srmacklem				}
1979191783Srmacklem			    }
1980191783Srmacklem			}
1981191783Srmacklem			if (ndp != NULL)
1982191783Srmacklem			    TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list);
1983191783Srmacklem
1984191783Srmacklem			/* and reclaim all byte range locks */
1985191783Srmacklem			lp = LIST_FIRST(&op->nfso_lock);
1986191783Srmacklem			while (lp != NULL) {
1987191783Srmacklem			    nlp = LIST_NEXT(lp, nfsl_list);
1988191783Srmacklem			    lp->nfsl_seqid = 0;
1989191783Srmacklem			    firstlock = 1;
1990191783Srmacklem			    lop = LIST_FIRST(&lp->nfsl_lock);
1991191783Srmacklem			    while (lop != NULL) {
1992191783Srmacklem				nlop = LIST_NEXT(lop, nfslo_list);
1993191783Srmacklem				if (lop->nfslo_end == NFS64BITSSET)
1994191783Srmacklem				    len = NFS64BITSSET;
1995191783Srmacklem				else
1996191783Srmacklem				    len = lop->nfslo_end - lop->nfslo_first;
1997191783Srmacklem				if (error != NFSERR_NOGRACE)
1998191783Srmacklem				    error = nfscl_trylock(nmp, NULL,
1999191783Srmacklem					op->nfso_fh, op->nfso_fhlen, lp,
2000191783Srmacklem					firstlock, 1, lop->nfslo_first, len,
2001191783Srmacklem					lop->nfslo_type, tcred, p);
2002191783Srmacklem				if (error != 0)
2003191783Srmacklem				    nfscl_freelock(lop, 0);
2004191783Srmacklem				else
2005191783Srmacklem				    firstlock = 0;
2006191783Srmacklem				lop = nlop;
2007191783Srmacklem			    }
2008191783Srmacklem			    /* If no locks, but a lockowner, just delete it. */
2009191783Srmacklem			    if (LIST_EMPTY(&lp->nfsl_lock))
2010191783Srmacklem				nfscl_freelockowner(lp, 0);
2011191783Srmacklem			    lp = nlp;
2012191783Srmacklem			}
2013191783Srmacklem		    } else {
2014191783Srmacklem			nfscl_freeopen(op, 0);
2015191783Srmacklem		    }
2016191783Srmacklem		}
2017191783Srmacklem		op = nop;
2018191783Srmacklem	    }
2019191783Srmacklem	    owp = nowp;
2020191783Srmacklem	}
2021191783Srmacklem
2022191783Srmacklem	/*
2023191783Srmacklem	 * Now, try and get any delegations not yet reclaimed by cobbling
2024191783Srmacklem	 * to-gether an appropriate open.
2025191783Srmacklem	 */
2026191783Srmacklem	nowp = NULL;
2027191783Srmacklem	dp = TAILQ_FIRST(&clp->nfsc_deleg);
2028191783Srmacklem	while (dp != NULL) {
2029191783Srmacklem	    ndp = TAILQ_NEXT(dp, nfsdl_list);
2030191783Srmacklem	    if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) {
2031191783Srmacklem		if (nowp == NULL) {
2032191783Srmacklem		    MALLOC(nowp, struct nfsclowner *,
2033191783Srmacklem			sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK);
2034191783Srmacklem		    /*
2035191783Srmacklem		     * Name must be as long an largest possible
2036191783Srmacklem		     * NFSV4CL_LOCKNAMELEN. 12 for now.
2037191783Srmacklem		     */
2038191783Srmacklem		    NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner,
2039191783Srmacklem			NFSV4CL_LOCKNAMELEN);
2040191783Srmacklem		    LIST_INIT(&nowp->nfsow_open);
2041191783Srmacklem		    nowp->nfsow_clp = clp;
2042191783Srmacklem		    nowp->nfsow_seqid = 0;
2043191783Srmacklem		    nowp->nfsow_defunct = 0;
2044191783Srmacklem		    nfscl_lockinit(&nowp->nfsow_rwlock);
2045191783Srmacklem		}
2046191783Srmacklem		nop = NULL;
2047191783Srmacklem		if (error != NFSERR_NOGRACE) {
2048191783Srmacklem		    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
2049191783Srmacklem			dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
2050191783Srmacklem		    nop->nfso_own = nowp;
2051191783Srmacklem		    if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
2052191783Srmacklem			nop->nfso_mode = NFSV4OPEN_ACCESSWRITE;
2053191783Srmacklem			delegtype = NFSV4OPEN_DELEGATEWRITE;
2054191783Srmacklem		    } else {
2055191783Srmacklem			nop->nfso_mode = NFSV4OPEN_ACCESSREAD;
2056191783Srmacklem			delegtype = NFSV4OPEN_DELEGATEREAD;
2057191783Srmacklem		    }
2058191783Srmacklem		    nop->nfso_opencnt = 0;
2059191783Srmacklem		    nop->nfso_posixlock = 1;
2060191783Srmacklem		    nop->nfso_fhlen = dp->nfsdl_fhlen;
2061191783Srmacklem		    NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen);
2062191783Srmacklem		    LIST_INIT(&nop->nfso_lock);
2063191783Srmacklem		    nop->nfso_stateid.seqid = 0;
2064191783Srmacklem		    nop->nfso_stateid.other[0] = 0;
2065191783Srmacklem		    nop->nfso_stateid.other[1] = 0;
2066191783Srmacklem		    nop->nfso_stateid.other[2] = 0;
2067191783Srmacklem		    newnfs_copycred(&dp->nfsdl_cred, tcred);
2068191783Srmacklem		    newnfs_copyincred(tcred, &nop->nfso_cred);
2069191783Srmacklem		    tdp = NULL;
2070191783Srmacklem		    error = nfscl_tryopen(nmp, NULL, nop->nfso_fh,
2071191783Srmacklem			nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen,
2072191783Srmacklem			nop->nfso_mode, nop, NULL, 0, &tdp, 1,
2073191783Srmacklem			delegtype, tcred, p);
2074191783Srmacklem		    if (tdp != NULL) {
2075191783Srmacklem			if ((tdp->nfsdl_flags & NFSCLDL_WRITE))
2076191783Srmacklem			    mode = NFSV4OPEN_ACCESSWRITE;
2077191783Srmacklem			else
2078191783Srmacklem			    mode = NFSV4OPEN_ACCESSREAD;
2079191783Srmacklem			if ((nop->nfso_mode & mode) == mode &&
2080191783Srmacklem			    nop->nfso_fhlen == tdp->nfsdl_fhlen &&
2081191783Srmacklem			    !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh,
2082191783Srmacklem			    nop->nfso_fhlen)) {
2083191783Srmacklem			    dp->nfsdl_stateid = tdp->nfsdl_stateid;
2084191783Srmacklem			    dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit;
2085191783Srmacklem			    dp->nfsdl_ace = tdp->nfsdl_ace;
2086191783Srmacklem			    dp->nfsdl_change = tdp->nfsdl_change;
2087191783Srmacklem			    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2088191783Srmacklem			    if ((tdp->nfsdl_flags & NFSCLDL_RECALL))
2089191783Srmacklem				dp->nfsdl_flags |= NFSCLDL_RECALL;
2090191783Srmacklem			    FREE((caddr_t)tdp, M_NFSCLDELEG);
2091191783Srmacklem			} else {
2092191783Srmacklem			    TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list);
2093191783Srmacklem			}
2094191783Srmacklem		    }
2095191783Srmacklem		}
2096191783Srmacklem		if (error) {
2097191783Srmacklem		    if (nop != NULL)
2098191783Srmacklem			FREE((caddr_t)nop, M_NFSCLOPEN);
2099191783Srmacklem		    /*
2100191783Srmacklem		     * Couldn't reclaim it, so throw the state
2101191783Srmacklem		     * away. Ouch!!
2102191783Srmacklem		     */
2103191783Srmacklem		    nfscl_cleandeleg(dp);
2104191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
2105191783Srmacklem		} else {
2106191783Srmacklem		    LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
2107191783Srmacklem		}
2108191783Srmacklem	    }
2109191783Srmacklem	    dp = ndp;
2110191783Srmacklem	}
2111191783Srmacklem
2112191783Srmacklem	/*
2113191783Srmacklem	 * Now, get rid of extra Opens and Delegations.
2114191783Srmacklem	 */
2115191783Srmacklem	LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) {
2116191783Srmacklem		do {
2117191783Srmacklem			newnfs_copycred(&op->nfso_cred, tcred);
2118191783Srmacklem			error = nfscl_tryclose(op, tcred, nmp, p);
2119191783Srmacklem			if (error == NFSERR_GRACE)
2120207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsexcls");
2121191783Srmacklem		} while (error == NFSERR_GRACE);
2122191783Srmacklem		LIST_REMOVE(op, nfso_list);
2123191783Srmacklem		FREE((caddr_t)op, M_NFSCLOPEN);
2124191783Srmacklem	}
2125191783Srmacklem	if (nowp != NULL)
2126191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
2127191783Srmacklem
2128191783Srmacklem	TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) {
2129191783Srmacklem		do {
2130191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, tcred);
2131191783Srmacklem			error = nfscl_trydelegreturn(dp, tcred, nmp, p);
2132191783Srmacklem			if (error == NFSERR_GRACE)
2133207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsexdlg");
2134191783Srmacklem		} while (error == NFSERR_GRACE);
2135191783Srmacklem		TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list);
2136191783Srmacklem		FREE((caddr_t)dp, M_NFSCLDELEG);
2137191783Srmacklem	}
2138191783Srmacklem
2139191783Srmacklem	NFSLOCKCLSTATE();
2140206818Srmacklem	clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG;
2141206818Srmacklem	wakeup(&clp->nfsc_flags);
2142191783Srmacklem	nfsv4_unlock(&clp->nfsc_lock, 0);
2143191783Srmacklem	NFSUNLOCKCLSTATE();
2144191783Srmacklem	NFSFREECRED(tcred);
2145191783Srmacklem}
2146191783Srmacklem
2147191783Srmacklem/*
2148191783Srmacklem * This function is called when a server replies with NFSERR_EXPIRED.
2149191783Srmacklem * It deletes all state for the client and does a fresh SetClientId/confirm.
2150191783Srmacklem * XXX Someday it should post a signal to the process(es) that hold the
2151191783Srmacklem * state, so they know that lock state has been lost.
2152191783Srmacklem */
2153191783SrmacklemAPPLESTATIC int
2154191783Srmacklemnfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
2155191783Srmacklem{
2156191783Srmacklem	struct nfsmount *nmp;
2157191783Srmacklem	struct ucred *cred;
2158191783Srmacklem	int igotlock = 0, error, trycnt;
2159191783Srmacklem
2160191783Srmacklem	/*
2161191783Srmacklem	 * If the clientid has gone away or a new SetClientid has already
2162191783Srmacklem	 * been done, just return ok.
2163191783Srmacklem	 */
2164191783Srmacklem	if (clp == NULL || clidrev != clp->nfsc_clientidrev)
2165191783Srmacklem		return (0);
2166191783Srmacklem
2167191783Srmacklem	/*
2168191783Srmacklem	 * First, lock the client structure, so everyone else will
2169191783Srmacklem	 * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so
2170191783Srmacklem	 * that only one thread does the work.
2171191783Srmacklem	 */
2172191783Srmacklem	NFSLOCKCLSTATE();
2173191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
2174191783Srmacklem	do {
2175191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2176222389Srmacklem		    NFSCLSTATEMUTEXPTR, NULL);
2177191783Srmacklem	} while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
2178191783Srmacklem	if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
2179191783Srmacklem		if (igotlock)
2180191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
2181191783Srmacklem		NFSUNLOCKCLSTATE();
2182191783Srmacklem		return (0);
2183191783Srmacklem	}
2184206880Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
2185191783Srmacklem	NFSUNLOCKCLSTATE();
2186191783Srmacklem
2187191783Srmacklem	nmp = clp->nfsc_nmp;
2188191783Srmacklem	if (nmp == NULL)
2189191783Srmacklem		panic("nfscl expired");
2190191783Srmacklem	cred = newnfs_getcred();
2191191783Srmacklem	trycnt = 5;
2192191783Srmacklem	do {
2193191783Srmacklem		error = nfsrpc_setclient(nmp, clp, cred, p);
2194191783Srmacklem	} while ((error == NFSERR_STALECLIENTID ||
2195191783Srmacklem	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
2196191783Srmacklem	if (error) {
2197191783Srmacklem		/*
2198191783Srmacklem		 * Clear out any state.
2199191783Srmacklem		 */
2200191783Srmacklem		nfscl_cleanclient(clp);
2201206880Srmacklem		NFSLOCKCLSTATE();
2202191783Srmacklem		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
2203191783Srmacklem		    NFSCLFLAGS_RECOVER);
2204191783Srmacklem	} else {
2205191783Srmacklem		/*
2206191783Srmacklem		 * Expire the state for the client.
2207191783Srmacklem		 */
2208191783Srmacklem		nfscl_expireclient(clp, nmp, cred, p);
2209206880Srmacklem		NFSLOCKCLSTATE();
2210191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
2211191783Srmacklem		clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2212191783Srmacklem	}
2213206880Srmacklem	clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG);
2214206880Srmacklem	wakeup(&clp->nfsc_flags);
2215191783Srmacklem	nfsv4_unlock(&clp->nfsc_lock, 0);
2216191783Srmacklem	NFSUNLOCKCLSTATE();
2217206880Srmacklem	NFSFREECRED(cred);
2218191783Srmacklem	return (error);
2219191783Srmacklem}
2220191783Srmacklem
2221191783Srmacklem/*
2222191783Srmacklem * This function inserts a lock in the list after insert_lop.
2223191783Srmacklem */
2224191783Srmacklemstatic void
2225191783Srmacklemnfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop,
2226191783Srmacklem    struct nfscllock *insert_lop, int local)
2227191783Srmacklem{
2228191783Srmacklem
2229191783Srmacklem	if ((struct nfscllockowner *)insert_lop == lp)
2230191783Srmacklem		LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list);
2231191783Srmacklem	else
2232191783Srmacklem		LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list);
2233191783Srmacklem	if (local)
2234191783Srmacklem		newnfsstats.cllocallocks++;
2235191783Srmacklem	else
2236191783Srmacklem		newnfsstats.cllocks++;
2237191783Srmacklem}
2238191783Srmacklem
2239191783Srmacklem/*
2240191783Srmacklem * This function updates the locking for a lock owner and given file. It
2241191783Srmacklem * maintains a list of lock ranges ordered on increasing file offset that
2242191783Srmacklem * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style).
2243191783Srmacklem * It always adds new_lop to the list and sometimes uses the one pointed
2244191783Srmacklem * at by other_lopp.
2245191783Srmacklem * Returns 1 if the locks were modified, 0 otherwise.
2246191783Srmacklem */
2247191783Srmacklemstatic int
2248191783Srmacklemnfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp,
2249191783Srmacklem    struct nfscllock **other_lopp, int local)
2250191783Srmacklem{
2251191783Srmacklem	struct nfscllock *new_lop = *new_lopp;
2252191783Srmacklem	struct nfscllock *lop, *tlop, *ilop;
2253191783Srmacklem	struct nfscllock *other_lop;
2254191783Srmacklem	int unlock = 0, modified = 0;
2255191783Srmacklem	u_int64_t tmp;
2256191783Srmacklem
2257191783Srmacklem	/*
2258191783Srmacklem	 * Work down the list until the lock is merged.
2259191783Srmacklem	 */
2260191783Srmacklem	if (new_lop->nfslo_type == F_UNLCK)
2261191783Srmacklem		unlock = 1;
2262191783Srmacklem	ilop = (struct nfscllock *)lp;
2263191783Srmacklem	lop = LIST_FIRST(&lp->nfsl_lock);
2264191783Srmacklem	while (lop != NULL) {
2265191783Srmacklem	    /*
2266191783Srmacklem	     * Only check locks for this file that aren't before the start of
2267191783Srmacklem	     * new lock's range.
2268191783Srmacklem	     */
2269191783Srmacklem	    if (lop->nfslo_end >= new_lop->nfslo_first) {
2270191783Srmacklem		if (new_lop->nfslo_end < lop->nfslo_first) {
2271191783Srmacklem		    /*
2272191783Srmacklem		     * If the new lock ends before the start of the
2273191783Srmacklem		     * current lock's range, no merge, just insert
2274191783Srmacklem		     * the new lock.
2275191783Srmacklem		     */
2276191783Srmacklem		    break;
2277191783Srmacklem		}
2278191783Srmacklem		if (new_lop->nfslo_type == lop->nfslo_type ||
2279191783Srmacklem		    (new_lop->nfslo_first <= lop->nfslo_first &&
2280191783Srmacklem		     new_lop->nfslo_end >= lop->nfslo_end)) {
2281191783Srmacklem		    /*
2282191783Srmacklem		     * This lock can be absorbed by the new lock/unlock.
2283191783Srmacklem		     * This happens when it covers the entire range
2284191783Srmacklem		     * of the old lock or is contiguous
2285191783Srmacklem		     * with the old lock and is of the same type or an
2286191783Srmacklem		     * unlock.
2287191783Srmacklem		     */
2288191783Srmacklem		    if (new_lop->nfslo_type != lop->nfslo_type ||
2289191783Srmacklem			new_lop->nfslo_first != lop->nfslo_first ||
2290191783Srmacklem			new_lop->nfslo_end != lop->nfslo_end)
2291191783Srmacklem			modified = 1;
2292191783Srmacklem		    if (lop->nfslo_first < new_lop->nfslo_first)
2293191783Srmacklem			new_lop->nfslo_first = lop->nfslo_first;
2294191783Srmacklem		    if (lop->nfslo_end > new_lop->nfslo_end)
2295191783Srmacklem			new_lop->nfslo_end = lop->nfslo_end;
2296191783Srmacklem		    tlop = lop;
2297191783Srmacklem		    lop = LIST_NEXT(lop, nfslo_list);
2298191783Srmacklem		    nfscl_freelock(tlop, local);
2299191783Srmacklem		    continue;
2300191783Srmacklem		}
2301191783Srmacklem
2302191783Srmacklem		/*
2303191783Srmacklem		 * All these cases are for contiguous locks that are not the
2304191783Srmacklem		 * same type, so they can't be merged.
2305191783Srmacklem		 */
2306191783Srmacklem		if (new_lop->nfslo_first <= lop->nfslo_first) {
2307191783Srmacklem		    /*
2308191783Srmacklem		     * This case is where the new lock overlaps with the
2309191783Srmacklem		     * first part of the old lock. Move the start of the
2310191783Srmacklem		     * old lock to just past the end of the new lock. The
2311191783Srmacklem		     * new lock will be inserted in front of the old, since
2312191783Srmacklem		     * ilop hasn't been updated. (We are done now.)
2313191783Srmacklem		     */
2314191783Srmacklem		    if (lop->nfslo_first != new_lop->nfslo_end) {
2315191783Srmacklem			lop->nfslo_first = new_lop->nfslo_end;
2316191783Srmacklem			modified = 1;
2317191783Srmacklem		    }
2318191783Srmacklem		    break;
2319191783Srmacklem		}
2320191783Srmacklem		if (new_lop->nfslo_end >= lop->nfslo_end) {
2321191783Srmacklem		    /*
2322191783Srmacklem		     * This case is where the new lock overlaps with the
2323191783Srmacklem		     * end of the old lock's range. Move the old lock's
2324191783Srmacklem		     * end to just before the new lock's first and insert
2325191783Srmacklem		     * the new lock after the old lock.
2326191783Srmacklem		     * Might not be done yet, since the new lock could
2327191783Srmacklem		     * overlap further locks with higher ranges.
2328191783Srmacklem		     */
2329191783Srmacklem		    if (lop->nfslo_end != new_lop->nfslo_first) {
2330191783Srmacklem			lop->nfslo_end = new_lop->nfslo_first;
2331191783Srmacklem			modified = 1;
2332191783Srmacklem		    }
2333191783Srmacklem		    ilop = lop;
2334191783Srmacklem		    lop = LIST_NEXT(lop, nfslo_list);
2335191783Srmacklem		    continue;
2336191783Srmacklem		}
2337191783Srmacklem		/*
2338191783Srmacklem		 * The final case is where the new lock's range is in the
2339191783Srmacklem		 * middle of the current lock's and splits the current lock
2340191783Srmacklem		 * up. Use *other_lopp to handle the second part of the
2341191783Srmacklem		 * split old lock range. (We are done now.)
2342191783Srmacklem		 * For unlock, we use new_lop as other_lop and tmp, since
2343191783Srmacklem		 * other_lop and new_lop are the same for this case.
2344191783Srmacklem		 * We noted the unlock case above, so we don't need
2345191783Srmacklem		 * new_lop->nfslo_type any longer.
2346191783Srmacklem		 */
2347191783Srmacklem		tmp = new_lop->nfslo_first;
2348191783Srmacklem		if (unlock) {
2349191783Srmacklem		    other_lop = new_lop;
2350191783Srmacklem		    *new_lopp = NULL;
2351191783Srmacklem		} else {
2352191783Srmacklem		    other_lop = *other_lopp;
2353191783Srmacklem		    *other_lopp = NULL;
2354191783Srmacklem		}
2355191783Srmacklem		other_lop->nfslo_first = new_lop->nfslo_end;
2356191783Srmacklem		other_lop->nfslo_end = lop->nfslo_end;
2357191783Srmacklem		other_lop->nfslo_type = lop->nfslo_type;
2358191783Srmacklem		lop->nfslo_end = tmp;
2359191783Srmacklem		nfscl_insertlock(lp, other_lop, lop, local);
2360191783Srmacklem		ilop = lop;
2361191783Srmacklem		modified = 1;
2362191783Srmacklem		break;
2363191783Srmacklem	    }
2364191783Srmacklem	    ilop = lop;
2365191783Srmacklem	    lop = LIST_NEXT(lop, nfslo_list);
2366191783Srmacklem	    if (lop == NULL)
2367191783Srmacklem		break;
2368191783Srmacklem	}
2369191783Srmacklem
2370191783Srmacklem	/*
2371191783Srmacklem	 * Insert the new lock in the list at the appropriate place.
2372191783Srmacklem	 */
2373191783Srmacklem	if (!unlock) {
2374191783Srmacklem		nfscl_insertlock(lp, new_lop, ilop, local);
2375191783Srmacklem		*new_lopp = NULL;
2376191783Srmacklem		modified = 1;
2377191783Srmacklem	}
2378191783Srmacklem	return (modified);
2379191783Srmacklem}
2380191783Srmacklem
2381191783Srmacklem/*
2382191783Srmacklem * This function must be run as a kernel thread.
2383191783Srmacklem * It does Renew Ops and recovery, when required.
2384191783Srmacklem */
2385191783SrmacklemAPPLESTATIC void
2386191783Srmacklemnfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
2387191783Srmacklem{
2388191783Srmacklem	struct nfsclowner *owp, *nowp;
2389191783Srmacklem	struct nfsclopen *op;
2390229752Srmacklem	struct nfscllockowner *lp, *nlp;
2391191783Srmacklem	struct nfscldeleghead dh;
2392191783Srmacklem	struct nfscldeleg *dp, *ndp;
2393191783Srmacklem	struct ucred *cred;
2394191783Srmacklem	u_int32_t clidrev;
2395191783Srmacklem	int error, cbpathdown, islept, igotlock, ret, clearok;
2396206818Srmacklem	uint32_t recover_done_time = 0;
2397247502Sjhb	time_t mytime;
2398229678Srmacklem	static time_t prevsec = 0;
2399229752Srmacklem	struct nfscllockownerfh *lfhp, *nlfhp;
2400229752Srmacklem	struct nfscllockownerfhhead lfh;
2401191783Srmacklem
2402191783Srmacklem	cred = newnfs_getcred();
2403206690Srmacklem	NFSLOCKCLSTATE();
2404191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD;
2405206690Srmacklem	NFSUNLOCKCLSTATE();
2406191783Srmacklem	for(;;) {
2407191783Srmacklem		newnfs_setroot(cred);
2408191783Srmacklem		cbpathdown = 0;
2409206818Srmacklem		if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) {
2410206818Srmacklem			/*
2411206818Srmacklem			 * Only allow one recover within 1/2 of the lease
2412206818Srmacklem			 * duration (nfsc_renew).
2413206818Srmacklem			 */
2414206818Srmacklem			if (recover_done_time < NFSD_MONOSEC) {
2415206818Srmacklem				recover_done_time = NFSD_MONOSEC +
2416206818Srmacklem				    clp->nfsc_renew;
2417206818Srmacklem				nfscl_recover(clp, cred, p);
2418206818Srmacklem			} else {
2419206818Srmacklem				NFSLOCKCLSTATE();
2420206818Srmacklem				clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2421206818Srmacklem				NFSUNLOCKCLSTATE();
2422206818Srmacklem			}
2423206818Srmacklem		}
2424191783Srmacklem		if (clp->nfsc_expire <= NFSD_MONOSEC &&
2425191783Srmacklem		    (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
2426191783Srmacklem			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
2427191783Srmacklem			clidrev = clp->nfsc_clientidrev;
2428191783Srmacklem			error = nfsrpc_renew(clp, cred, p);
2429191783Srmacklem			if (error == NFSERR_CBPATHDOWN)
2430191783Srmacklem			    cbpathdown = 1;
2431206690Srmacklem			else if (error == NFSERR_STALECLIENTID) {
2432206690Srmacklem			    NFSLOCKCLSTATE();
2433191783Srmacklem			    clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2434206690Srmacklem			    NFSUNLOCKCLSTATE();
2435206690Srmacklem			} else if (error == NFSERR_EXPIRED)
2436191783Srmacklem			    (void) nfscl_hasexpired(clp, clidrev, p);
2437191783Srmacklem		}
2438191783Srmacklem
2439191783Srmacklem		TAILQ_INIT(&dh);
2440191783Srmacklem		NFSLOCKCLSTATE();
2441191783Srmacklem		if (cbpathdown)
2442191783Srmacklem			/* It's a Total Recall! */
2443191783Srmacklem			nfscl_totalrecall(clp);
2444191783Srmacklem
2445191783Srmacklem		/*
2446191783Srmacklem		 * Now, handle defunct owners.
2447191783Srmacklem		 */
2448229752Srmacklem		LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
2449229752Srmacklem			if (LIST_EMPTY(&owp->nfsow_open)) {
2450229752Srmacklem				if (owp->nfsow_defunct != 0)
2451229752Srmacklem					nfscl_freeopenowner(owp, 0);
2452191783Srmacklem			}
2453191783Srmacklem		}
2454191783Srmacklem
2455191783Srmacklem		/*
2456191783Srmacklem		 * Do the recall on any delegations. To avoid trouble, always
2457191783Srmacklem		 * come back up here after having slept.
2458191783Srmacklem		 */
2459191783Srmacklem		igotlock = 0;
2460191783Srmacklemtryagain:
2461191783Srmacklem		dp = TAILQ_FIRST(&clp->nfsc_deleg);
2462191783Srmacklem		while (dp != NULL) {
2463191783Srmacklem			ndp = TAILQ_NEXT(dp, nfsdl_list);
2464191783Srmacklem			if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
2465191783Srmacklem				/*
2466191783Srmacklem				 * Wait for outstanding I/O ops to be done.
2467191783Srmacklem				 */
2468191783Srmacklem				if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
2469191783Srmacklem				    if (igotlock) {
2470191783Srmacklem					nfsv4_unlock(&clp->nfsc_lock, 0);
2471191783Srmacklem					igotlock = 0;
2472191783Srmacklem				    }
2473191783Srmacklem				    dp->nfsdl_rwlock.nfslock_lock |=
2474191783Srmacklem					NFSV4LOCK_WANTED;
2475191783Srmacklem				    (void) nfsmsleep(&dp->nfsdl_rwlock,
2476191783Srmacklem					NFSCLSTATEMUTEXPTR, PZERO, "nfscld",
2477191783Srmacklem					NULL);
2478191783Srmacklem				    goto tryagain;
2479191783Srmacklem				}
2480191783Srmacklem				while (!igotlock) {
2481191783Srmacklem				    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
2482222389Srmacklem					&islept, NFSCLSTATEMUTEXPTR, NULL);
2483191783Srmacklem				    if (islept)
2484191783Srmacklem					goto tryagain;
2485191783Srmacklem				}
2486191783Srmacklem				NFSUNLOCKCLSTATE();
2487191783Srmacklem				newnfs_copycred(&dp->nfsdl_cred, cred);
2488191783Srmacklem				ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
2489207082Srmacklem				    NULL, cred, p, 1);
2490191783Srmacklem				if (!ret) {
2491191783Srmacklem				    nfscl_cleandeleg(dp);
2492191783Srmacklem				    TAILQ_REMOVE(&clp->nfsc_deleg, dp,
2493191783Srmacklem					nfsdl_list);
2494191783Srmacklem				    LIST_REMOVE(dp, nfsdl_hash);
2495191783Srmacklem				    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2496191783Srmacklem				    nfscl_delegcnt--;
2497191783Srmacklem				    newnfsstats.cldelegates--;
2498191783Srmacklem				}
2499191783Srmacklem				NFSLOCKCLSTATE();
2500191783Srmacklem			}
2501191783Srmacklem			dp = ndp;
2502191783Srmacklem		}
2503191783Srmacklem
2504191783Srmacklem		/*
2505191783Srmacklem		 * Clear out old delegations, if we are above the high water
2506191783Srmacklem		 * mark. Only clear out ones with no state related to them.
2507191783Srmacklem		 * The tailq list is in LRU order.
2508191783Srmacklem		 */
2509191783Srmacklem		dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
2510191783Srmacklem		while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) {
2511191783Srmacklem		    ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
2512191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
2513191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock == 0 &&
2514191783Srmacklem			dp->nfsdl_timestamp < NFSD_MONOSEC &&
2515214406Srmacklem			(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
2516214406Srmacklem			  NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) {
2517191783Srmacklem			clearok = 1;
2518191783Srmacklem			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2519191783Srmacklem			    op = LIST_FIRST(&owp->nfsow_open);
2520195819Srmacklem			    if (op != NULL) {
2521191783Srmacklem				clearok = 0;
2522191783Srmacklem				break;
2523191783Srmacklem			    }
2524191783Srmacklem			}
2525191783Srmacklem			if (clearok) {
2526191783Srmacklem			    LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
2527191783Srmacklem				if (!LIST_EMPTY(&lp->nfsl_lock)) {
2528191783Srmacklem				    clearok = 0;
2529191783Srmacklem				    break;
2530191783Srmacklem				}
2531191783Srmacklem			    }
2532191783Srmacklem			}
2533191783Srmacklem			if (clearok) {
2534191783Srmacklem			    TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
2535191783Srmacklem			    LIST_REMOVE(dp, nfsdl_hash);
2536191783Srmacklem			    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2537191783Srmacklem			    nfscl_delegcnt--;
2538191783Srmacklem			    newnfsstats.cldelegates--;
2539191783Srmacklem			}
2540191783Srmacklem		    }
2541191783Srmacklem		    dp = ndp;
2542191783Srmacklem		}
2543191783Srmacklem		if (igotlock)
2544191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
2545191783Srmacklem		NFSUNLOCKCLSTATE();
2546191783Srmacklem
2547191783Srmacklem		/*
2548191783Srmacklem		 * Delegreturn any delegations cleaned out or recalled.
2549191783Srmacklem		 */
2550191783Srmacklem		TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
2551191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
2552191783Srmacklem			(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2553191783Srmacklem			TAILQ_REMOVE(&dh, dp, nfsdl_list);
2554191783Srmacklem			FREE((caddr_t)dp, M_NFSCLDELEG);
2555191783Srmacklem		}
2556191783Srmacklem
2557229752Srmacklem		SLIST_INIT(&lfh);
2558191783Srmacklem		/*
2559229599Srmacklem		 * Call nfscl_cleanupkext() once per second to check for
2560229599Srmacklem		 * open/lock owners where the process has exited.
2561191783Srmacklem		 */
2562247502Sjhb		mytime = NFSD_MONOSEC;
2563247502Sjhb		if (prevsec != mytime) {
2564247502Sjhb			prevsec = mytime;
2565229752Srmacklem			nfscl_cleanupkext(clp, &lfh);
2566191783Srmacklem		}
2567191783Srmacklem
2568229752Srmacklem		/*
2569229752Srmacklem		 * Do a ReleaseLockOwner for all lock owners where the
2570229752Srmacklem		 * associated process no longer exists, as found by
2571229752Srmacklem		 * nfscl_cleanupkext().
2572229752Srmacklem		 */
2573229752Srmacklem		newnfs_setroot(cred);
2574229752Srmacklem		SLIST_FOREACH_SAFE(lfhp, &lfh, nfslfh_list, nlfhp) {
2575229752Srmacklem			LIST_FOREACH_SAFE(lp, &lfhp->nfslfh_lock, nfsl_list,
2576229752Srmacklem			    nlp) {
2577229752Srmacklem				(void)nfsrpc_rellockown(clp->nfsc_nmp, lp,
2578229752Srmacklem				    lfhp->nfslfh_fh, lfhp->nfslfh_len, cred,
2579229752Srmacklem				    p);
2580229752Srmacklem				nfscl_freelockowner(lp, 0);
2581229752Srmacklem			}
2582229752Srmacklem			free(lfhp, M_TEMP);
2583229752Srmacklem		}
2584229752Srmacklem		SLIST_INIT(&lfh);
2585229752Srmacklem
2586222389Srmacklem		NFSLOCKCLSTATE();
2587191783Srmacklem		if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
2588222389Srmacklem			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl",
2589222389Srmacklem			    hz);
2590191783Srmacklem		if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
2591222389Srmacklem			clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
2592222389Srmacklem			NFSUNLOCKCLSTATE();
2593191783Srmacklem			NFSFREECRED(cred);
2594191783Srmacklem			wakeup((caddr_t)clp);
2595191783Srmacklem			return;
2596191783Srmacklem		}
2597222389Srmacklem		NFSUNLOCKCLSTATE();
2598191783Srmacklem	}
2599191783Srmacklem}
2600191783Srmacklem
2601191783Srmacklem/*
2602191783Srmacklem * Initiate state recovery. Called when NFSERR_STALECLIENTID or
2603191783Srmacklem * NFSERR_STALESTATEID is received.
2604191783Srmacklem */
2605191783SrmacklemAPPLESTATIC void
2606191783Srmacklemnfscl_initiate_recovery(struct nfsclclient *clp)
2607191783Srmacklem{
2608191783Srmacklem
2609191783Srmacklem	if (clp == NULL)
2610191783Srmacklem		return;
2611191783Srmacklem	NFSLOCKCLSTATE();
2612191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2613191783Srmacklem	NFSUNLOCKCLSTATE();
2614191783Srmacklem	wakeup((caddr_t)clp);
2615191783Srmacklem}
2616191783Srmacklem
2617191783Srmacklem/*
2618191783Srmacklem * Dump out the state stuff for debugging.
2619191783Srmacklem */
2620191783SrmacklemAPPLESTATIC void
2621191783Srmacklemnfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
2622191783Srmacklem    int lockowner, int locks)
2623191783Srmacklem{
2624191783Srmacklem	struct nfsclclient *clp;
2625191783Srmacklem	struct nfsclowner *owp;
2626191783Srmacklem	struct nfsclopen *op;
2627191783Srmacklem	struct nfscllockowner *lp;
2628191783Srmacklem	struct nfscllock *lop;
2629191783Srmacklem	struct nfscldeleg *dp;
2630191783Srmacklem
2631191783Srmacklem	clp = nmp->nm_clp;
2632191783Srmacklem	if (clp == NULL) {
2633191783Srmacklem		printf("nfscl dumpstate NULL clp\n");
2634191783Srmacklem		return;
2635191783Srmacklem	}
2636191783Srmacklem	NFSLOCKCLSTATE();
2637191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2638191783Srmacklem	  LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2639191783Srmacklem	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2640191783Srmacklem		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2641191783Srmacklem		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2642191783Srmacklem		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2643191783Srmacklem		    owp->nfsow_seqid);
2644191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2645191783Srmacklem		if (opens)
2646191783Srmacklem		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2647191783Srmacklem			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2648191783Srmacklem			op->nfso_stateid.other[2], op->nfso_opencnt,
2649191783Srmacklem			op->nfso_fh[12]);
2650191783Srmacklem		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2651191783Srmacklem		    if (lockowner)
2652191783Srmacklem			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2653191783Srmacklem			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2654191783Srmacklem			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2655191783Srmacklem			    lp->nfsl_seqid,
2656191783Srmacklem			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2657191783Srmacklem			    lp->nfsl_stateid.other[2]);
2658191783Srmacklem		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2659191783Srmacklem			if (locks)
2660191783Srmacklem#ifdef __FreeBSD__
2661191783Srmacklem			    printf("lck typ=%d fst=%ju end=%ju\n",
2662191783Srmacklem				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2663191783Srmacklem				(intmax_t)lop->nfslo_end);
2664191783Srmacklem#else
2665191783Srmacklem			    printf("lck typ=%d fst=%qd end=%qd\n",
2666191783Srmacklem				lop->nfslo_type, lop->nfslo_first,
2667191783Srmacklem				lop->nfslo_end);
2668191783Srmacklem#endif
2669191783Srmacklem		    }
2670191783Srmacklem		}
2671191783Srmacklem	    }
2672191783Srmacklem	  }
2673191783Srmacklem	}
2674191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2675191783Srmacklem	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2676191783Srmacklem		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2677191783Srmacklem		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2678191783Srmacklem		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2679191783Srmacklem		    owp->nfsow_seqid);
2680191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2681191783Srmacklem		if (opens)
2682191783Srmacklem		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2683191783Srmacklem			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2684191783Srmacklem			op->nfso_stateid.other[2], op->nfso_opencnt,
2685191783Srmacklem			op->nfso_fh[12]);
2686191783Srmacklem		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2687191783Srmacklem		    if (lockowner)
2688191783Srmacklem			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2689191783Srmacklem			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2690191783Srmacklem			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2691191783Srmacklem			    lp->nfsl_seqid,
2692191783Srmacklem			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2693191783Srmacklem			    lp->nfsl_stateid.other[2]);
2694191783Srmacklem		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2695191783Srmacklem			if (locks)
2696191783Srmacklem#ifdef __FreeBSD__
2697191783Srmacklem			    printf("lck typ=%d fst=%ju end=%ju\n",
2698191783Srmacklem				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2699191783Srmacklem				(intmax_t)lop->nfslo_end);
2700191783Srmacklem#else
2701191783Srmacklem			    printf("lck typ=%d fst=%qd end=%qd\n",
2702191783Srmacklem				lop->nfslo_type, lop->nfslo_first,
2703191783Srmacklem				lop->nfslo_end);
2704191783Srmacklem#endif
2705191783Srmacklem		    }
2706191783Srmacklem		}
2707191783Srmacklem	    }
2708191783Srmacklem	}
2709191783Srmacklem	NFSUNLOCKCLSTATE();
2710191783Srmacklem}
2711191783Srmacklem
2712191783Srmacklem/*
2713191783Srmacklem * Check for duplicate open owners and opens.
2714191783Srmacklem * (Only used as a diagnostic aid.)
2715191783Srmacklem */
2716191783SrmacklemAPPLESTATIC void
2717191783Srmacklemnfscl_dupopen(vnode_t vp, int dupopens)
2718191783Srmacklem{
2719191783Srmacklem	struct nfsclclient *clp;
2720191783Srmacklem	struct nfsclowner *owp, *owp2;
2721191783Srmacklem	struct nfsclopen *op, *op2;
2722191783Srmacklem	struct nfsfh *nfhp;
2723191783Srmacklem
2724191783Srmacklem	clp = VFSTONFS(vnode_mount(vp))->nm_clp;
2725191783Srmacklem	if (clp == NULL) {
2726191783Srmacklem		printf("nfscl dupopen NULL clp\n");
2727191783Srmacklem		return;
2728191783Srmacklem	}
2729191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
2730191783Srmacklem	NFSLOCKCLSTATE();
2731191783Srmacklem
2732191783Srmacklem	/*
2733191783Srmacklem	 * First, search for duplicate owners.
2734191783Srmacklem	 * These should never happen!
2735191783Srmacklem	 */
2736191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2737191783Srmacklem	    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2738191783Srmacklem		if (owp != owp2 &&
2739191783Srmacklem		    !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
2740191783Srmacklem		    NFSV4CL_LOCKNAMELEN)) {
2741191783Srmacklem			NFSUNLOCKCLSTATE();
2742191783Srmacklem			printf("DUP OWNER\n");
2743191783Srmacklem			nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0);
2744191783Srmacklem			return;
2745191783Srmacklem		}
2746191783Srmacklem	    }
2747191783Srmacklem	}
2748191783Srmacklem
2749191783Srmacklem	/*
2750191783Srmacklem	 * Now, search for duplicate stateids.
2751191783Srmacklem	 * These shouldn't happen, either.
2752191783Srmacklem	 */
2753191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2754191783Srmacklem	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2755191783Srmacklem		LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2756191783Srmacklem		    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2757191783Srmacklem			if (op != op2 &&
2758191783Srmacklem			    (op->nfso_stateid.other[0] != 0 ||
2759191783Srmacklem			     op->nfso_stateid.other[1] != 0 ||
2760191783Srmacklem			     op->nfso_stateid.other[2] != 0) &&
2761191783Srmacklem			    op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
2762191783Srmacklem			    op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
2763191783Srmacklem			    op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
2764191783Srmacklem			    NFSUNLOCKCLSTATE();
2765191783Srmacklem			    printf("DUP STATEID\n");
2766191783Srmacklem			    nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0,
2767191783Srmacklem				0);
2768191783Srmacklem			    return;
2769191783Srmacklem			}
2770191783Srmacklem		    }
2771191783Srmacklem		}
2772191783Srmacklem	    }
2773191783Srmacklem	}
2774191783Srmacklem
2775191783Srmacklem	/*
2776191783Srmacklem	 * Now search for duplicate opens.
2777191783Srmacklem	 * Duplicate opens for the same owner
2778191783Srmacklem	 * should never occur. Other duplicates are
2779191783Srmacklem	 * possible and are checked for if "dupopens"
2780191783Srmacklem	 * is true.
2781191783Srmacklem	 */
2782191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2783191783Srmacklem	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2784191783Srmacklem		if (nfhp->nfh_len == op2->nfso_fhlen &&
2785191783Srmacklem		    !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
2786191783Srmacklem		    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2787191783Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2788191783Srmacklem			    if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
2789191783Srmacklem				!NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
2790191783Srmacklem				(!NFSBCMP(op->nfso_own->nfsow_owner,
2791191783Srmacklem				 op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
2792191783Srmacklem				 dupopens)) {
2793191783Srmacklem				if (!NFSBCMP(op->nfso_own->nfsow_owner,
2794191783Srmacklem				    op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
2795191783Srmacklem				    NFSUNLOCKCLSTATE();
2796191783Srmacklem				    printf("BADDUP OPEN\n");
2797191783Srmacklem				} else {
2798191783Srmacklem				    NFSUNLOCKCLSTATE();
2799191783Srmacklem				    printf("DUP OPEN\n");
2800191783Srmacklem				}
2801191783Srmacklem				nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1,
2802191783Srmacklem				    0, 0);
2803191783Srmacklem				return;
2804191783Srmacklem			    }
2805191783Srmacklem			}
2806191783Srmacklem		    }
2807191783Srmacklem		}
2808191783Srmacklem	    }
2809191783Srmacklem	}
2810191783Srmacklem	NFSUNLOCKCLSTATE();
2811191783Srmacklem}
2812191783Srmacklem
2813191783Srmacklem/*
2814191783Srmacklem * During close, find an open that needs to be dereferenced and
2815191783Srmacklem * dereference it. If there are no more opens for this file,
2816195510Srmacklem * log a message to that effect.
2817195510Srmacklem * Opens aren't actually Close'd until VOP_INACTIVE() is performed
2818195510Srmacklem * on the file's vnode.
2819191783Srmacklem * This is the safe way, since it is difficult to identify
2820195510Srmacklem * which open the close is for and I/O can be performed after the
2821195510Srmacklem * close(2) system call when a file is mmap'd.
2822191783Srmacklem * If it returns 0 for success, there will be a referenced
2823195510Srmacklem * clp returned via clpp.
2824191783Srmacklem */
2825191783SrmacklemAPPLESTATIC int
2826195510Srmacklemnfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
2827191783Srmacklem{
2828191783Srmacklem	struct nfsclclient *clp;
2829195510Srmacklem	struct nfsclowner *owp;
2830195510Srmacklem	struct nfsclopen *op;
2831191783Srmacklem	struct nfscldeleg *dp;
2832191783Srmacklem	struct nfsfh *nfhp;
2833195510Srmacklem	int error, notdecr;
2834191783Srmacklem
2835192337Srmacklem	error = nfscl_getcl(vp, NULL, NULL, &clp);
2836191783Srmacklem	if (error)
2837191783Srmacklem		return (error);
2838191783Srmacklem	*clpp = clp;
2839191783Srmacklem
2840191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
2841191783Srmacklem	notdecr = 1;
2842191783Srmacklem	NFSLOCKCLSTATE();
2843191783Srmacklem	/*
2844191783Srmacklem	 * First, look for one under a delegation that was locally issued
2845191783Srmacklem	 * and just decrement the opencnt for it. Since all my Opens against
2846191783Srmacklem	 * the server are DENY_NONE, I don't see a problem with hanging
2847191783Srmacklem	 * onto them. (It is much easier to use one of the extant Opens
2848191783Srmacklem	 * that I already have on the server when a Delegation is recalled
2849195510Srmacklem	 * than to do fresh Opens.) Someday, I might need to rethink this, but.
2850191783Srmacklem	 */
2851191783Srmacklem	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
2852191783Srmacklem	if (dp != NULL) {
2853191783Srmacklem		LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2854191783Srmacklem			op = LIST_FIRST(&owp->nfsow_open);
2855191783Srmacklem			if (op != NULL) {
2856191783Srmacklem				/*
2857191783Srmacklem				 * Since a delegation is for a file, there
2858191783Srmacklem				 * should never be more than one open for
2859191783Srmacklem				 * each openowner.
2860191783Srmacklem				 */
2861191783Srmacklem				if (LIST_NEXT(op, nfso_list) != NULL)
2862191783Srmacklem					panic("nfscdeleg opens");
2863191783Srmacklem				if (notdecr && op->nfso_opencnt > 0) {
2864191783Srmacklem					notdecr = 0;
2865191783Srmacklem					op->nfso_opencnt--;
2866191783Srmacklem					break;
2867191783Srmacklem				}
2868191783Srmacklem			}
2869191783Srmacklem		}
2870191783Srmacklem	}
2871191783Srmacklem
2872191783Srmacklem	/* Now process the opens against the server. */
2873191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2874195510Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2875192337Srmacklem			if (op->nfso_fhlen == nfhp->nfh_len &&
2876192337Srmacklem			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
2877192337Srmacklem			    nfhp->nfh_len)) {
2878192337Srmacklem				/* Found an open, decrement cnt if possible */
2879192337Srmacklem				if (notdecr && op->nfso_opencnt > 0) {
2880192337Srmacklem					notdecr = 0;
2881192337Srmacklem					op->nfso_opencnt--;
2882192337Srmacklem				}
2883192337Srmacklem				/*
2884195510Srmacklem				 * There are more opens, so just return.
2885192337Srmacklem				 */
2886192337Srmacklem				if (op->nfso_opencnt > 0) {
2887192337Srmacklem					NFSUNLOCKCLSTATE();
2888192337Srmacklem					return (0);
2889192337Srmacklem				}
2890191783Srmacklem			}
2891191783Srmacklem		}
2892191783Srmacklem	}
2893195510Srmacklem	NFSUNLOCKCLSTATE();
2894195510Srmacklem	if (notdecr)
2895195510Srmacklem		printf("nfscl: never fnd open\n");
2896195510Srmacklem	return (0);
2897195510Srmacklem}
2898191783Srmacklem
2899195510SrmacklemAPPLESTATIC int
2900195510Srmacklemnfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
2901195510Srmacklem{
2902195510Srmacklem	struct nfsclclient *clp;
2903195510Srmacklem	struct nfsclowner *owp, *nowp;
2904195510Srmacklem	struct nfsclopen *op;
2905195510Srmacklem	struct nfscldeleg *dp;
2906195510Srmacklem	struct nfsfh *nfhp;
2907195510Srmacklem	int error;
2908195510Srmacklem
2909195510Srmacklem	error = nfscl_getcl(vp, NULL, NULL, &clp);
2910195510Srmacklem	if (error)
2911195510Srmacklem		return (error);
2912195510Srmacklem	*clpp = clp;
2913195510Srmacklem
2914195510Srmacklem	nfhp = VTONFS(vp)->n_fhp;
2915195510Srmacklem	NFSLOCKCLSTATE();
2916195510Srmacklem	/*
2917195510Srmacklem	 * First get rid of the local Open structures, which should be no
2918195510Srmacklem	 * longer in use.
2919195510Srmacklem	 */
2920195510Srmacklem	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
2921195510Srmacklem	if (dp != NULL) {
2922195510Srmacklem		LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
2923191783Srmacklem			op = LIST_FIRST(&owp->nfsow_open);
2924195510Srmacklem			if (op != NULL) {
2925195510Srmacklem				KASSERT((op->nfso_opencnt == 0),
2926195510Srmacklem				    ("nfscl: bad open cnt on deleg"));
2927195510Srmacklem				nfscl_freeopen(op, 1);
2928191783Srmacklem			}
2929195510Srmacklem			nfscl_freeopenowner(owp, 1);
2930191783Srmacklem		}
2931195510Srmacklem	}
2932195510Srmacklem
2933195510Srmacklem	/* Now process the opens against the server. */
2934195510Srmacklemlookformore:
2935195510Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2936195510Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
2937195510Srmacklem		while (op != NULL) {
2938195510Srmacklem			if (op->nfso_fhlen == nfhp->nfh_len &&
2939195510Srmacklem			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
2940195510Srmacklem			    nfhp->nfh_len)) {
2941195510Srmacklem				/* Found an open, close it. */
2942195510Srmacklem				KASSERT((op->nfso_opencnt == 0),
2943195510Srmacklem				    ("nfscl: bad open cnt on server"));
2944195510Srmacklem				NFSUNLOCKCLSTATE();
2945195510Srmacklem				nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op,
2946195510Srmacklem				    p);
2947195510Srmacklem				NFSLOCKCLSTATE();
2948195510Srmacklem				goto lookformore;
2949191783Srmacklem			}
2950195510Srmacklem			op = LIST_NEXT(op, nfso_list);
2951191783Srmacklem		}
2952191783Srmacklem	}
2953191783Srmacklem	NFSUNLOCKCLSTATE();
2954191783Srmacklem	return (0);
2955191783Srmacklem}
2956191783Srmacklem
2957191783Srmacklem/*
2958191783Srmacklem * Return all delegations on this client.
2959191783Srmacklem * (Must be called with client sleep lock.)
2960191783Srmacklem */
2961191783Srmacklemstatic void
2962191783Srmacklemnfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
2963191783Srmacklem{
2964191783Srmacklem	struct nfscldeleg *dp, *ndp;
2965191783Srmacklem	struct ucred *cred;
2966191783Srmacklem
2967191783Srmacklem	cred = newnfs_getcred();
2968191783Srmacklem	TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
2969191783Srmacklem		nfscl_cleandeleg(dp);
2970191783Srmacklem		(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2971191783Srmacklem		nfscl_freedeleg(&clp->nfsc_deleg, dp);
2972191783Srmacklem	}
2973191783Srmacklem	NFSFREECRED(cred);
2974191783Srmacklem}
2975191783Srmacklem
2976191783Srmacklem/*
2977191783Srmacklem * Do a callback RPC.
2978191783Srmacklem */
2979191783SrmacklemAPPLESTATIC void
2980191783Srmacklemnfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
2981191783Srmacklem{
2982191783Srmacklem	int i, op;
2983191783Srmacklem	u_int32_t *tl;
2984191783Srmacklem	struct nfsclclient *clp;
2985191783Srmacklem	struct nfscldeleg *dp = NULL;
2986191783Srmacklem	int numops, taglen = -1, error = 0, trunc, ret = 0;
2987191783Srmacklem	u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident;
2988191783Srmacklem	u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
2989191783Srmacklem	vnode_t vp = NULL;
2990191783Srmacklem	struct nfsnode *np;
2991191783Srmacklem	struct vattr va;
2992191783Srmacklem	struct nfsfh *nfhp;
2993191783Srmacklem	mount_t mp;
2994191783Srmacklem	nfsattrbit_t attrbits, rattrbits;
2995191783Srmacklem	nfsv4stateid_t stateid;
2996191783Srmacklem
2997191783Srmacklem	nfsrvd_rephead(nd);
2998191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2999191783Srmacklem	taglen = fxdr_unsigned(int, *tl);
3000191783Srmacklem	if (taglen < 0) {
3001191783Srmacklem		error = EBADRPC;
3002191783Srmacklem		goto nfsmout;
3003191783Srmacklem	}
3004191783Srmacklem	if (taglen <= NFSV4_SMALLSTR)
3005191783Srmacklem		tagstr = tag;
3006191783Srmacklem	else
3007191783Srmacklem		tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
3008191783Srmacklem	error = nfsrv_mtostr(nd, tagstr, taglen);
3009191783Srmacklem	if (error) {
3010191783Srmacklem		if (taglen > NFSV4_SMALLSTR)
3011191783Srmacklem			free(tagstr, M_TEMP);
3012191783Srmacklem		taglen = -1;
3013191783Srmacklem		goto nfsmout;
3014191783Srmacklem	}
3015191783Srmacklem	(void) nfsm_strtom(nd, tag, taglen);
3016191783Srmacklem	if (taglen > NFSV4_SMALLSTR) {
3017191783Srmacklem		free(tagstr, M_TEMP);
3018191783Srmacklem	}
3019191783Srmacklem	NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
3020191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3021191783Srmacklem	minorvers = fxdr_unsigned(u_int32_t, *tl++);
3022191783Srmacklem	if (minorvers != NFSV4_MINORVERSION)
3023191783Srmacklem		nd->nd_repstat = NFSERR_MINORVERMISMATCH;
3024191783Srmacklem	cbident = fxdr_unsigned(u_int32_t, *tl++);
3025191783Srmacklem	if (nd->nd_repstat)
3026191783Srmacklem		numops = 0;
3027191783Srmacklem	else
3028191783Srmacklem		numops = fxdr_unsigned(int, *tl);
3029191783Srmacklem	/*
3030191783Srmacklem	 * Loop around doing the sub ops.
3031191783Srmacklem	 */
3032191783Srmacklem	for (i = 0; i < numops; i++) {
3033191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3034191783Srmacklem		NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
3035191783Srmacklem		*repp++ = *tl;
3036191783Srmacklem		op = fxdr_unsigned(int, *tl);
3037191783Srmacklem		if (op < NFSV4OP_CBGETATTR || op > NFSV4OP_CBRECALL) {
3038191783Srmacklem		    nd->nd_repstat = NFSERR_OPILLEGAL;
3039191783Srmacklem		    *repp = nfscl_errmap(nd);
3040191783Srmacklem		    retops++;
3041191783Srmacklem		    break;
3042191783Srmacklem		}
3043191783Srmacklem		nd->nd_procnum = op;
3044191783Srmacklem		newnfsstats.cbrpccnt[nd->nd_procnum]++;
3045191783Srmacklem		switch (op) {
3046191783Srmacklem		case NFSV4OP_CBGETATTR:
3047191783Srmacklem			clp = NULL;
3048191783Srmacklem			error = nfsm_getfh(nd, &nfhp);
3049191783Srmacklem			if (!error)
3050191783Srmacklem				error = nfsrv_getattrbits(nd, &attrbits,
3051191783Srmacklem				    NULL, NULL);
3052191783Srmacklem			if (!error) {
3053191783Srmacklem				mp = nfscl_getmnt(cbident);
3054191783Srmacklem				if (mp == NULL)
3055191783Srmacklem					error = NFSERR_SERVERFAULT;
3056191783Srmacklem			}
3057191783Srmacklem			if (!error) {
3058191783Srmacklem				dp = NULL;
3059191783Srmacklem				NFSLOCKCLSTATE();
3060191783Srmacklem				clp = nfscl_findcl(VFSTONFS(mp));
3061191783Srmacklem				if (clp != NULL)
3062191783Srmacklem					dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3063191783Srmacklem					    nfhp->nfh_len);
3064191783Srmacklem				NFSUNLOCKCLSTATE();
3065191783Srmacklem				if (dp == NULL)
3066191783Srmacklem					error = NFSERR_SERVERFAULT;
3067191783Srmacklem			}
3068191783Srmacklem			if (!error) {
3069191783Srmacklem				ret = nfscl_ngetreopen(mp, nfhp->nfh_fh,
3070191783Srmacklem				    nfhp->nfh_len, p, &np);
3071191783Srmacklem				if (!ret)
3072191783Srmacklem					vp = NFSTOV(np);
3073191783Srmacklem			}
3074191783Srmacklem			if (nfhp != NULL)
3075191783Srmacklem				FREE((caddr_t)nfhp, M_NFSFH);
3076191783Srmacklem			if (!error) {
3077191783Srmacklem				NFSZERO_ATTRBIT(&rattrbits);
3078191783Srmacklem				if (NFSISSET_ATTRBIT(&attrbits,
3079191783Srmacklem				    NFSATTRBIT_SIZE)) {
3080191783Srmacklem					if (!ret)
3081191783Srmacklem						va.va_size = np->n_size;
3082191783Srmacklem					else
3083191783Srmacklem						va.va_size = dp->nfsdl_size;
3084191783Srmacklem					NFSSETBIT_ATTRBIT(&rattrbits,
3085191783Srmacklem					    NFSATTRBIT_SIZE);
3086191783Srmacklem				}
3087191783Srmacklem				if (NFSISSET_ATTRBIT(&attrbits,
3088191783Srmacklem				    NFSATTRBIT_CHANGE)) {
3089191783Srmacklem					va.va_filerev = dp->nfsdl_change;
3090191783Srmacklem					if (ret || (np->n_flag & NDELEGMOD))
3091191783Srmacklem						va.va_filerev++;
3092191783Srmacklem					NFSSETBIT_ATTRBIT(&rattrbits,
3093191783Srmacklem					    NFSATTRBIT_CHANGE);
3094191783Srmacklem				}
3095220645Srmacklem				(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
3096260172Srmacklem				    NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0,
3097220645Srmacklem				    (uint64_t)0);
3098191783Srmacklem				if (!ret)
3099191783Srmacklem					vrele(vp);
3100191783Srmacklem			}
3101191783Srmacklem			break;
3102191783Srmacklem		case NFSV4OP_CBRECALL:
3103191783Srmacklem			clp = NULL;
3104191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
3105191783Srmacklem			    NFSX_UNSIGNED);
3106191783Srmacklem			stateid.seqid = *tl++;
3107191783Srmacklem			NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
3108191783Srmacklem			    NFSX_STATEIDOTHER);
3109191783Srmacklem			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3110191783Srmacklem			trunc = fxdr_unsigned(int, *tl);
3111191783Srmacklem			error = nfsm_getfh(nd, &nfhp);
3112191783Srmacklem			if (!error) {
3113191783Srmacklem				mp = nfscl_getmnt(cbident);
3114191783Srmacklem				if (mp == NULL)
3115191783Srmacklem					error = NFSERR_SERVERFAULT;
3116191783Srmacklem			}
3117191783Srmacklem			if (!error) {
3118191783Srmacklem				NFSLOCKCLSTATE();
3119191783Srmacklem				clp = nfscl_findcl(VFSTONFS(mp));
3120191783Srmacklem				if (clp != NULL) {
3121191783Srmacklem					dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3122191783Srmacklem					    nfhp->nfh_len);
3123214406Srmacklem					if (dp != NULL && (dp->nfsdl_flags &
3124214406Srmacklem					    NFSCLDL_DELEGRET) == 0) {
3125191783Srmacklem						dp->nfsdl_flags |=
3126191783Srmacklem						    NFSCLDL_RECALL;
3127191783Srmacklem						wakeup((caddr_t)clp);
3128191783Srmacklem					}
3129191783Srmacklem				} else {
3130191783Srmacklem					error = NFSERR_SERVERFAULT;
3131191783Srmacklem				}
3132191783Srmacklem				NFSUNLOCKCLSTATE();
3133191783Srmacklem			}
3134191783Srmacklem			if (nfhp != NULL)
3135191783Srmacklem				FREE((caddr_t)nfhp, M_NFSFH);
3136191783Srmacklem			break;
3137191783Srmacklem		};
3138191783Srmacklem		if (error) {
3139191783Srmacklem			if (error == EBADRPC || error == NFSERR_BADXDR) {
3140191783Srmacklem				nd->nd_repstat = NFSERR_BADXDR;
3141191783Srmacklem			} else {
3142191783Srmacklem				nd->nd_repstat = error;
3143191783Srmacklem			}
3144191783Srmacklem			error = 0;
3145191783Srmacklem		}
3146191783Srmacklem		retops++;
3147191783Srmacklem		if (nd->nd_repstat) {
3148191783Srmacklem			*repp = nfscl_errmap(nd);
3149191783Srmacklem			break;
3150191783Srmacklem		} else
3151191783Srmacklem			*repp = 0;	/* NFS4_OK */
3152191783Srmacklem	}
3153191783Srmacklemnfsmout:
3154191783Srmacklem	if (error) {
3155191783Srmacklem		if (error == EBADRPC || error == NFSERR_BADXDR)
3156191783Srmacklem			nd->nd_repstat = NFSERR_BADXDR;
3157191783Srmacklem		else
3158191783Srmacklem			printf("nfsv4 comperr1=%d\n", error);
3159191783Srmacklem	}
3160191783Srmacklem	if (taglen == -1) {
3161191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3162191783Srmacklem		*tl++ = 0;
3163191783Srmacklem		*tl = 0;
3164191783Srmacklem	} else {
3165191783Srmacklem		*retopsp = txdr_unsigned(retops);
3166191783Srmacklem	}
3167191783Srmacklem	*nd->nd_errp = nfscl_errmap(nd);
3168191783Srmacklem}
3169191783Srmacklem
3170191783Srmacklem/*
3171191783Srmacklem * Generate the next cbident value. Basically just increment a static value
3172191783Srmacklem * and then check that it isn't already in the list, if it has wrapped around.
3173191783Srmacklem */
3174191783Srmacklemstatic u_int32_t
3175191783Srmacklemnfscl_nextcbident(void)
3176191783Srmacklem{
3177191783Srmacklem	struct nfsclclient *clp;
3178191783Srmacklem	int matched;
3179191783Srmacklem	static u_int32_t nextcbident = 0;
3180191783Srmacklem	static int haswrapped = 0;
3181191783Srmacklem
3182191783Srmacklem	nextcbident++;
3183191783Srmacklem	if (nextcbident == 0)
3184191783Srmacklem		haswrapped = 1;
3185191783Srmacklem	if (haswrapped) {
3186191783Srmacklem		/*
3187191783Srmacklem		 * Search the clientid list for one already using this cbident.
3188191783Srmacklem		 */
3189191783Srmacklem		do {
3190191783Srmacklem			matched = 0;
3191191783Srmacklem			NFSLOCKCLSTATE();
3192191783Srmacklem			LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3193191783Srmacklem				if (clp->nfsc_cbident == nextcbident) {
3194191783Srmacklem					matched = 1;
3195191783Srmacklem					break;
3196191783Srmacklem				}
3197191783Srmacklem			}
3198191783Srmacklem			NFSUNLOCKCLSTATE();
3199191783Srmacklem			if (matched == 1)
3200191783Srmacklem				nextcbident++;
3201191783Srmacklem		} while (matched);
3202191783Srmacklem	}
3203191783Srmacklem	return (nextcbident);
3204191783Srmacklem}
3205191783Srmacklem
3206191783Srmacklem/*
3207191783Srmacklem * Get the mount point related to a given cbident.
3208191783Srmacklem */
3209191783Srmacklemstatic mount_t
3210191783Srmacklemnfscl_getmnt(u_int32_t cbident)
3211191783Srmacklem{
3212191783Srmacklem	struct nfsclclient *clp;
3213191783Srmacklem	struct nfsmount *nmp;
3214191783Srmacklem
3215191783Srmacklem	NFSLOCKCLSTATE();
3216191783Srmacklem	LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3217191783Srmacklem		if (clp->nfsc_cbident == cbident)
3218191783Srmacklem			break;
3219191783Srmacklem	}
3220191783Srmacklem	if (clp == NULL) {
3221191783Srmacklem		NFSUNLOCKCLSTATE();
3222191783Srmacklem		return (NULL);
3223191783Srmacklem	}
3224191783Srmacklem	nmp = clp->nfsc_nmp;
3225191783Srmacklem	NFSUNLOCKCLSTATE();
3226191783Srmacklem	return (nmp->nm_mountp);
3227191783Srmacklem}
3228191783Srmacklem
3229191783Srmacklem/*
3230191783Srmacklem * Search for a lock conflict locally on the client. A conflict occurs if
3231191783Srmacklem * - not same owner and overlapping byte range and at least one of them is
3232191783Srmacklem *   a write lock or this is an unlock.
3233191783Srmacklem */
3234191783Srmacklemstatic int
3235201439Srmacklemnfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
3236201439Srmacklem    struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
3237201439Srmacklem    struct nfscllock **lopp)
3238191783Srmacklem{
3239191783Srmacklem	struct nfsclowner *owp;
3240191783Srmacklem	struct nfsclopen *op;
3241191783Srmacklem	int ret;
3242191783Srmacklem
3243191783Srmacklem	if (dp != NULL) {
3244191783Srmacklem		ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
3245191783Srmacklem		if (ret)
3246191783Srmacklem			return (ret);
3247191783Srmacklem	}
3248191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3249191783Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3250201439Srmacklem			if (op->nfso_fhlen == fhlen &&
3251201439Srmacklem			    !NFSBCMP(op->nfso_fh, fhp, fhlen)) {
3252201439Srmacklem				ret = nfscl_checkconflict(&op->nfso_lock, nlop,
3253201439Srmacklem				    own, lopp);
3254201439Srmacklem				if (ret)
3255201439Srmacklem					return (ret);
3256201439Srmacklem			}
3257191783Srmacklem		}
3258191783Srmacklem	}
3259191783Srmacklem	return (0);
3260191783Srmacklem}
3261191783Srmacklem
3262191783Srmacklemstatic int
3263191783Srmacklemnfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
3264191783Srmacklem    u_int8_t *own, struct nfscllock **lopp)
3265191783Srmacklem{
3266191783Srmacklem	struct nfscllockowner *lp;
3267191783Srmacklem	struct nfscllock *lop;
3268191783Srmacklem
3269191783Srmacklem	LIST_FOREACH(lp, lhp, nfsl_list) {
3270191783Srmacklem		if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
3271191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3272191783Srmacklem				if (lop->nfslo_first >= nlop->nfslo_end)
3273191783Srmacklem					break;
3274191783Srmacklem				if (lop->nfslo_end <= nlop->nfslo_first)
3275191783Srmacklem					continue;
3276191783Srmacklem				if (lop->nfslo_type == F_WRLCK ||
3277191783Srmacklem				    nlop->nfslo_type == F_WRLCK ||
3278191783Srmacklem				    nlop->nfslo_type == F_UNLCK) {
3279191783Srmacklem					if (lopp != NULL)
3280191783Srmacklem						*lopp = lop;
3281191783Srmacklem					return (NFSERR_DENIED);
3282191783Srmacklem				}
3283191783Srmacklem			}
3284191783Srmacklem		}
3285191783Srmacklem	}
3286191783Srmacklem	return (0);
3287191783Srmacklem}
3288191783Srmacklem
3289191783Srmacklem/*
3290191783Srmacklem * Check for a local conflicting lock.
3291191783Srmacklem */
3292191783SrmacklemAPPLESTATIC int
3293191783Srmacklemnfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
3294222719Srmacklem    u_int64_t len, struct flock *fl, NFSPROC_T *p, void *id, int flags)
3295191783Srmacklem{
3296191783Srmacklem	struct nfscllock *lop, nlck;
3297191783Srmacklem	struct nfscldeleg *dp;
3298191783Srmacklem	struct nfsnode *np;
3299191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
3300191783Srmacklem	int error;
3301191783Srmacklem
3302191783Srmacklem	nlck.nfslo_type = fl->l_type;
3303191783Srmacklem	nlck.nfslo_first = off;
3304191783Srmacklem	if (len == NFS64BITSSET) {
3305191783Srmacklem		nlck.nfslo_end = NFS64BITSSET;
3306191783Srmacklem	} else {
3307191783Srmacklem		nlck.nfslo_end = off + len;
3308191783Srmacklem		if (nlck.nfslo_end <= nlck.nfslo_first)
3309191783Srmacklem			return (NFSERR_INVAL);
3310191783Srmacklem	}
3311191783Srmacklem	np = VTONFS(vp);
3312222719Srmacklem	nfscl_filllockowner(id, own, flags);
3313191783Srmacklem	NFSLOCKCLSTATE();
3314191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3315201439Srmacklem	error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
3316201439Srmacklem	    &nlck, own, dp, &lop);
3317201439Srmacklem	if (error != 0) {
3318191783Srmacklem		fl->l_whence = SEEK_SET;
3319191783Srmacklem		fl->l_start = lop->nfslo_first;
3320191783Srmacklem		if (lop->nfslo_end == NFS64BITSSET)
3321191783Srmacklem			fl->l_len = 0;
3322191783Srmacklem		else
3323191783Srmacklem			fl->l_len = lop->nfslo_end - lop->nfslo_first;
3324191783Srmacklem		fl->l_pid = (pid_t)0;
3325191783Srmacklem		fl->l_type = lop->nfslo_type;
3326201439Srmacklem		error = -1;			/* no RPC required */
3327191783Srmacklem	} else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
3328191783Srmacklem	    fl->l_type == F_RDLCK)) {
3329191783Srmacklem		/*
3330191783Srmacklem		 * The delegation ensures that there isn't a conflicting
3331191783Srmacklem		 * lock on the server, so return -1 to indicate an RPC
3332191783Srmacklem		 * isn't required.
3333191783Srmacklem		 */
3334191783Srmacklem		fl->l_type = F_UNLCK;
3335191783Srmacklem		error = -1;
3336191783Srmacklem	}
3337191783Srmacklem	NFSUNLOCKCLSTATE();
3338191783Srmacklem	return (error);
3339191783Srmacklem}
3340191783Srmacklem
3341191783Srmacklem/*
3342191783Srmacklem * Handle Recall of a delegation.
3343191783Srmacklem * The clp must be exclusive locked when this is called.
3344191783Srmacklem */
3345191783Srmacklemstatic int
3346191783Srmacklemnfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
3347207082Srmacklem    struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p,
3348207082Srmacklem    int called_from_renewthread)
3349191783Srmacklem{
3350191783Srmacklem	struct nfsclowner *owp, *lowp, *nowp;
3351191783Srmacklem	struct nfsclopen *op, *lop;
3352191783Srmacklem	struct nfscllockowner *lp;
3353191783Srmacklem	struct nfscllock *lckp;
3354191783Srmacklem	struct nfsnode *np;
3355191783Srmacklem	int error = 0, ret, gotvp = 0;
3356191783Srmacklem
3357191783Srmacklem	if (vp == NULL) {
3358191783Srmacklem		/*
3359191783Srmacklem		 * First, get a vnode for the file. This is needed to do RPCs.
3360191783Srmacklem		 */
3361191783Srmacklem		ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
3362191783Srmacklem		    dp->nfsdl_fhlen, p, &np);
3363191783Srmacklem		if (ret) {
3364191783Srmacklem			/*
3365191783Srmacklem			 * File isn't open, so nothing to move over to the
3366191783Srmacklem			 * server.
3367191783Srmacklem			 */
3368191783Srmacklem			return (0);
3369191783Srmacklem		}
3370191783Srmacklem		vp = NFSTOV(np);
3371191783Srmacklem		gotvp = 1;
3372191783Srmacklem	} else {
3373191783Srmacklem		np = VTONFS(vp);
3374191783Srmacklem	}
3375191783Srmacklem	dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
3376191783Srmacklem
3377191783Srmacklem	/*
3378191783Srmacklem	 * Ok, if it's a write delegation, flush data to the server, so
3379191783Srmacklem	 * that close/open consistency is retained.
3380191783Srmacklem	 */
3381207082Srmacklem	ret = 0;
3382191783Srmacklem	NFSLOCKNODE(np);
3383191783Srmacklem	if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
3384191783Srmacklem		np->n_flag |= NDELEGRECALL;
3385191783Srmacklem		NFSUNLOCKNODE(np);
3386207082Srmacklem		ret = ncl_flush(vp, MNT_WAIT, cred, p, 1,
3387207082Srmacklem		    called_from_renewthread);
3388191783Srmacklem		NFSLOCKNODE(np);
3389214406Srmacklem		np->n_flag &= ~NDELEGRECALL;
3390191783Srmacklem	}
3391214406Srmacklem	NFSINVALATTRCACHE(np);
3392191783Srmacklem	NFSUNLOCKNODE(np);
3393207082Srmacklem	if (ret == EIO && called_from_renewthread != 0) {
3394207082Srmacklem		/*
3395207082Srmacklem		 * If the flush failed with EIO for the renew thread,
3396207082Srmacklem		 * return now, so that the dirty buffer will be flushed
3397207082Srmacklem		 * later.
3398207082Srmacklem		 */
3399207082Srmacklem		if (gotvp != 0)
3400207082Srmacklem			vrele(vp);
3401207082Srmacklem		return (ret);
3402207082Srmacklem	}
3403191783Srmacklem
3404191783Srmacklem	/*
3405191783Srmacklem	 * Now, for each openowner with opens issued locally, move them
3406191783Srmacklem	 * over to state against the server.
3407191783Srmacklem	 */
3408191783Srmacklem	LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
3409191783Srmacklem		lop = LIST_FIRST(&lowp->nfsow_open);
3410191783Srmacklem		if (lop != NULL) {
3411191783Srmacklem			if (LIST_NEXT(lop, nfso_list) != NULL)
3412191783Srmacklem				panic("nfsdlg mult opens");
3413191783Srmacklem			/*
3414191783Srmacklem			 * Look for the same openowner against the server.
3415191783Srmacklem			 */
3416191783Srmacklem			LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3417191783Srmacklem				if (!NFSBCMP(lowp->nfsow_owner,
3418191783Srmacklem				    owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
3419191783Srmacklem					newnfs_copycred(&dp->nfsdl_cred, cred);
3420191783Srmacklem					ret = nfscl_moveopen(vp, clp, nmp, lop,
3421191783Srmacklem					    owp, dp, cred, p);
3422191783Srmacklem					if (ret == NFSERR_STALECLIENTID ||
3423191783Srmacklem					    ret == NFSERR_STALEDONTRECOVER) {
3424191783Srmacklem						if (gotvp)
3425191783Srmacklem							vrele(vp);
3426191783Srmacklem						return (ret);
3427191783Srmacklem					}
3428191783Srmacklem					if (ret) {
3429191783Srmacklem						nfscl_freeopen(lop, 1);
3430191783Srmacklem						if (!error)
3431191783Srmacklem							error = ret;
3432191783Srmacklem					}
3433191783Srmacklem					break;
3434191783Srmacklem				}
3435191783Srmacklem			}
3436191783Srmacklem
3437191783Srmacklem			/*
3438191783Srmacklem			 * If no openowner found, create one and get an open
3439191783Srmacklem			 * for it.
3440191783Srmacklem			 */
3441191783Srmacklem			if (owp == NULL) {
3442191783Srmacklem				MALLOC(nowp, struct nfsclowner *,
3443191783Srmacklem				    sizeof (struct nfsclowner), M_NFSCLOWNER,
3444191783Srmacklem				    M_WAITOK);
3445191783Srmacklem				nfscl_newopen(clp, NULL, &owp, &nowp, &op,
3446191783Srmacklem				    NULL, lowp->nfsow_owner, dp->nfsdl_fh,
3447191783Srmacklem				    dp->nfsdl_fhlen, NULL);
3448191783Srmacklem				newnfs_copycred(&dp->nfsdl_cred, cred);
3449191783Srmacklem				ret = nfscl_moveopen(vp, clp, nmp, lop,
3450191783Srmacklem				    owp, dp, cred, p);
3451191783Srmacklem				if (ret) {
3452191783Srmacklem					nfscl_freeopenowner(owp, 0);
3453191783Srmacklem					if (ret == NFSERR_STALECLIENTID ||
3454191783Srmacklem					    ret == NFSERR_STALEDONTRECOVER) {
3455191783Srmacklem						if (gotvp)
3456191783Srmacklem							vrele(vp);
3457191783Srmacklem						return (ret);
3458191783Srmacklem					}
3459191783Srmacklem					if (ret) {
3460191783Srmacklem						nfscl_freeopen(lop, 1);
3461191783Srmacklem						if (!error)
3462191783Srmacklem							error = ret;
3463191783Srmacklem					}
3464191783Srmacklem				}
3465191783Srmacklem			}
3466191783Srmacklem		}
3467191783Srmacklem	}
3468191783Srmacklem
3469191783Srmacklem	/*
3470191783Srmacklem	 * Now, get byte range locks for any locks done locally.
3471191783Srmacklem	 */
3472191783Srmacklem	LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3473191783Srmacklem		LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
3474191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
3475191783Srmacklem			ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
3476191783Srmacklem			if (ret == NFSERR_STALESTATEID ||
3477191783Srmacklem			    ret == NFSERR_STALEDONTRECOVER ||
3478191783Srmacklem			    ret == NFSERR_STALECLIENTID) {
3479191783Srmacklem				if (gotvp)
3480191783Srmacklem					vrele(vp);
3481191783Srmacklem				return (ret);
3482191783Srmacklem			}
3483191783Srmacklem			if (ret && !error)
3484191783Srmacklem				error = ret;
3485191783Srmacklem		}
3486191783Srmacklem	}
3487191783Srmacklem	if (gotvp)
3488191783Srmacklem		vrele(vp);
3489191783Srmacklem	return (error);
3490191783Srmacklem}
3491191783Srmacklem
3492191783Srmacklem/*
3493191783Srmacklem * Move a locally issued open over to an owner on the state list.
3494191783Srmacklem * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
3495191783Srmacklem * returns with it unlocked.
3496191783Srmacklem */
3497191783Srmacklemstatic int
3498191783Srmacklemnfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3499191783Srmacklem    struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
3500191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
3501191783Srmacklem{
3502191783Srmacklem	struct nfsclopen *op, *nop;
3503191783Srmacklem	struct nfscldeleg *ndp;
3504191783Srmacklem	struct nfsnode *np;
3505191783Srmacklem	int error = 0, newone;
3506191783Srmacklem
3507191783Srmacklem	/*
3508191783Srmacklem	 * First, look for an appropriate open, If found, just increment the
3509191783Srmacklem	 * opencnt in it.
3510191783Srmacklem	 */
3511191783Srmacklem	LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3512191783Srmacklem		if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
3513191783Srmacklem		    op->nfso_fhlen == lop->nfso_fhlen &&
3514191783Srmacklem		    !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
3515191783Srmacklem			op->nfso_opencnt += lop->nfso_opencnt;
3516191783Srmacklem			nfscl_freeopen(lop, 1);
3517191783Srmacklem			return (0);
3518191783Srmacklem		}
3519191783Srmacklem	}
3520191783Srmacklem
3521191783Srmacklem	/* No appropriate open, so we have to do one against the server. */
3522191783Srmacklem	np = VTONFS(vp);
3523191783Srmacklem	MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
3524191783Srmacklem	    lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
3525191783Srmacklem	newone = 0;
3526191783Srmacklem	nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
3527191783Srmacklem	    lop->nfso_fh, lop->nfso_fhlen, &newone);
3528191783Srmacklem	ndp = dp;
3529191783Srmacklem	error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen,
3530191783Srmacklem	    lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
3531191783Srmacklem	    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
3532191783Srmacklem	if (error) {
3533191783Srmacklem		if (newone)
3534191783Srmacklem			nfscl_freeopen(op, 0);
3535191783Srmacklem	} else {
3536191783Srmacklem		if (newone)
3537191783Srmacklem			newnfs_copyincred(cred, &op->nfso_cred);
3538191783Srmacklem		op->nfso_mode |= lop->nfso_mode;
3539191783Srmacklem		op->nfso_opencnt += lop->nfso_opencnt;
3540191783Srmacklem		nfscl_freeopen(lop, 1);
3541191783Srmacklem	}
3542191783Srmacklem	if (nop != NULL)
3543191783Srmacklem		FREE((caddr_t)nop, M_NFSCLOPEN);
3544191783Srmacklem	if (ndp != NULL) {
3545191783Srmacklem		/*
3546191783Srmacklem		 * What should I do with the returned delegation, since the
3547191783Srmacklem		 * delegation is being recalled? For now, just printf and
3548191783Srmacklem		 * through it away.
3549191783Srmacklem		 */
3550191783Srmacklem		printf("Moveopen returned deleg\n");
3551191783Srmacklem		FREE((caddr_t)ndp, M_NFSCLDELEG);
3552191783Srmacklem	}
3553191783Srmacklem	return (error);
3554191783Srmacklem}
3555191783Srmacklem
3556191783Srmacklem/*
3557191783Srmacklem * Recall all delegations on this client.
3558191783Srmacklem */
3559191783Srmacklemstatic void
3560191783Srmacklemnfscl_totalrecall(struct nfsclclient *clp)
3561191783Srmacklem{
3562191783Srmacklem	struct nfscldeleg *dp;
3563191783Srmacklem
3564214406Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
3565214406Srmacklem		if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0)
3566214406Srmacklem			dp->nfsdl_flags |= NFSCLDL_RECALL;
3567214406Srmacklem	}
3568191783Srmacklem}
3569191783Srmacklem
3570191783Srmacklem/*
3571191783Srmacklem * Relock byte ranges. Called for delegation recall and state expiry.
3572191783Srmacklem */
3573191783Srmacklemstatic int
3574191783Srmacklemnfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3575191783Srmacklem    struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
3576191783Srmacklem    NFSPROC_T *p)
3577191783Srmacklem{
3578191783Srmacklem	struct nfscllockowner *nlp;
3579191783Srmacklem	struct nfsfh *nfhp;
3580191783Srmacklem	u_int64_t off, len;
3581191783Srmacklem	u_int32_t clidrev = 0;
3582191783Srmacklem	int error, newone, donelocally;
3583191783Srmacklem
3584191783Srmacklem	off = lop->nfslo_first;
3585191783Srmacklem	len = lop->nfslo_end - lop->nfslo_first;
3586191783Srmacklem	error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
3587229752Srmacklem	    clp, 1, NULL, lp->nfsl_lockflags, lp->nfsl_owner,
3588229752Srmacklem	    lp->nfsl_openowner, &nlp, &newone, &donelocally);
3589191783Srmacklem	if (error || donelocally)
3590191783Srmacklem		return (error);
3591191783Srmacklem	if (nmp->nm_clp != NULL)
3592191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
3593191783Srmacklem	else
3594191783Srmacklem		clidrev = 0;
3595191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
3596191783Srmacklem	error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
3597191783Srmacklem	    nfhp->nfh_len, nlp, newone, 0, off,
3598191783Srmacklem	    len, lop->nfslo_type, cred, p);
3599191783Srmacklem	if (error)
3600191783Srmacklem		nfscl_freelockowner(nlp, 0);
3601191783Srmacklem	return (error);
3602191783Srmacklem}
3603191783Srmacklem
3604191783Srmacklem/*
3605191783Srmacklem * Called to re-open a file. Basically get a vnode for the file handle
3606191783Srmacklem * and then call nfsrpc_openrpc() to do the rest.
3607191783Srmacklem */
3608191783Srmacklemstatic int
3609191783Srmacklemnfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
3610191783Srmacklem    u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
3611191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
3612191783Srmacklem{
3613191783Srmacklem	struct nfsnode *np;
3614191783Srmacklem	vnode_t vp;
3615191783Srmacklem	int error;
3616191783Srmacklem
3617191783Srmacklem	error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
3618191783Srmacklem	if (error)
3619191783Srmacklem		return (error);
3620191783Srmacklem	vp = NFSTOV(np);
3621191783Srmacklem	if (np->n_v4 != NULL) {
3622191783Srmacklem		error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
3623191783Srmacklem		    np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
3624191783Srmacklem		    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
3625191783Srmacklem		    cred, p);
3626191783Srmacklem	} else {
3627191783Srmacklem		error = EINVAL;
3628191783Srmacklem	}
3629191783Srmacklem	vrele(vp);
3630191783Srmacklem	return (error);
3631191783Srmacklem}
3632191783Srmacklem
3633191783Srmacklem/*
3634191783Srmacklem * Try an open against the server. Just call nfsrpc_openrpc(), retrying while
3635191783Srmacklem * NFSERR_DELAY. Also, try system credentials, if the passed in credentials
3636191783Srmacklem * fail.
3637191783Srmacklem */
3638191783Srmacklemstatic int
3639191783Srmacklemnfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
3640191783Srmacklem    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
3641191783Srmacklem    u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
3642191783Srmacklem    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
3643191783Srmacklem{
3644191783Srmacklem	int error;
3645191783Srmacklem
3646191783Srmacklem	do {
3647191783Srmacklem		error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
3648191783Srmacklem		    mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
3649191783Srmacklem		    0, 0);
3650191783Srmacklem		if (error == NFSERR_DELAY)
3651207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfstryop");
3652191783Srmacklem	} while (error == NFSERR_DELAY);
3653191783Srmacklem	if (error == EAUTH || error == EACCES) {
3654191783Srmacklem		/* Try again using system credentials */
3655191783Srmacklem		newnfs_setroot(cred);
3656191783Srmacklem		do {
3657191783Srmacklem		    error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
3658191783Srmacklem			newfhlen, mode, op, name, namelen, ndpp, reclaim,
3659191783Srmacklem			delegtype, cred, p, 1, 0);
3660191783Srmacklem		    if (error == NFSERR_DELAY)
3661207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfstryop");
3662191783Srmacklem		} while (error == NFSERR_DELAY);
3663191783Srmacklem	}
3664191783Srmacklem	return (error);
3665191783Srmacklem}
3666191783Srmacklem
3667191783Srmacklem/*
3668191783Srmacklem * Try a byte range lock. Just loop on nfsrpc_lock() while it returns
3669191783Srmacklem * NFSERR_DELAY. Also, retry with system credentials, if the provided
3670191783Srmacklem * cred don't work.
3671191783Srmacklem */
3672191783Srmacklemstatic int
3673191783Srmacklemnfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
3674191783Srmacklem    int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
3675191783Srmacklem    u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
3676191783Srmacklem{
3677191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3678191783Srmacklem	int error;
3679191783Srmacklem
3680191783Srmacklem	do {
3681191783Srmacklem		error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
3682191783Srmacklem		    reclaim, off, len, type, cred, p, 0);
3683191783Srmacklem		if (!error && nd->nd_repstat == NFSERR_DELAY)
3684207170Srmacklem			(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3685207170Srmacklem			    "nfstrylck");
3686191783Srmacklem	} while (!error && nd->nd_repstat == NFSERR_DELAY);
3687191783Srmacklem	if (!error)
3688191783Srmacklem		error = nd->nd_repstat;
3689191783Srmacklem	if (error == EAUTH || error == EACCES) {
3690191783Srmacklem		/* Try again using root credentials */
3691191783Srmacklem		newnfs_setroot(cred);
3692191783Srmacklem		do {
3693191783Srmacklem			error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
3694191783Srmacklem			    newone, reclaim, off, len, type, cred, p, 1);
3695191783Srmacklem			if (!error && nd->nd_repstat == NFSERR_DELAY)
3696207170Srmacklem				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3697207170Srmacklem				    "nfstrylck");
3698191783Srmacklem		} while (!error && nd->nd_repstat == NFSERR_DELAY);
3699191783Srmacklem		if (!error)
3700191783Srmacklem			error = nd->nd_repstat;
3701191783Srmacklem	}
3702191783Srmacklem	return (error);
3703191783Srmacklem}
3704191783Srmacklem
3705191783Srmacklem/*
3706191783Srmacklem * Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
3707191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
3708191783Srmacklem * credentials fail.
3709191783Srmacklem */
3710191783Srmacklemstatic int
3711191783Srmacklemnfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
3712191783Srmacklem    struct nfsmount *nmp, NFSPROC_T *p)
3713191783Srmacklem{
3714191783Srmacklem	int error;
3715191783Srmacklem
3716191783Srmacklem	do {
3717191783Srmacklem		error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
3718191783Srmacklem		if (error == NFSERR_DELAY)
3719207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfstrydp");
3720191783Srmacklem	} while (error == NFSERR_DELAY);
3721191783Srmacklem	if (error == EAUTH || error == EACCES) {
3722191783Srmacklem		/* Try again using system credentials */
3723191783Srmacklem		newnfs_setroot(cred);
3724191783Srmacklem		do {
3725191783Srmacklem			error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
3726191783Srmacklem			if (error == NFSERR_DELAY)
3727207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfstrydp");
3728191783Srmacklem		} while (error == NFSERR_DELAY);
3729191783Srmacklem	}
3730191783Srmacklem	return (error);
3731191783Srmacklem}
3732191783Srmacklem
3733191783Srmacklem/*
3734191783Srmacklem * Try a close against the server. Just call nfsrpc_closerpc(),
3735191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
3736191783Srmacklem * credentials fail.
3737191783Srmacklem */
3738191783SrmacklemAPPLESTATIC int
3739191783Srmacklemnfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
3740191783Srmacklem    struct nfsmount *nmp, NFSPROC_T *p)
3741191783Srmacklem{
3742191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3743191783Srmacklem	int error;
3744191783Srmacklem
3745191783Srmacklem	do {
3746191783Srmacklem		error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
3747191783Srmacklem		if (error == NFSERR_DELAY)
3748207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfstrycl");
3749191783Srmacklem	} while (error == NFSERR_DELAY);
3750191783Srmacklem	if (error == EAUTH || error == EACCES) {
3751191783Srmacklem		/* Try again using system credentials */
3752191783Srmacklem		newnfs_setroot(cred);
3753191783Srmacklem		do {
3754191783Srmacklem			error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
3755191783Srmacklem			if (error == NFSERR_DELAY)
3756207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfstrycl");
3757191783Srmacklem		} while (error == NFSERR_DELAY);
3758191783Srmacklem	}
3759191783Srmacklem	return (error);
3760191783Srmacklem}
3761191783Srmacklem
3762191783Srmacklem/*
3763191783Srmacklem * Decide if a delegation on a file permits close without flushing writes
3764191783Srmacklem * to the server. This might be a big performance win in some environments.
3765191783Srmacklem * (Not useful until the client does caching on local stable storage.)
3766191783Srmacklem */
3767210786SrmacklemAPPLESTATIC int
3768191783Srmacklemnfscl_mustflush(vnode_t vp)
3769191783Srmacklem{
3770191783Srmacklem	struct nfsclclient *clp;
3771191783Srmacklem	struct nfscldeleg *dp;
3772191783Srmacklem	struct nfsnode *np;
3773191783Srmacklem	struct nfsmount *nmp;
3774191783Srmacklem
3775191783Srmacklem	np = VTONFS(vp);
3776191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
3777191783Srmacklem	if (!NFSHASNFSV4(nmp))
3778210786Srmacklem		return (1);
3779191783Srmacklem	NFSLOCKCLSTATE();
3780191783Srmacklem	clp = nfscl_findcl(nmp);
3781191783Srmacklem	if (clp == NULL) {
3782191783Srmacklem		NFSUNLOCKCLSTATE();
3783210786Srmacklem		return (1);
3784191783Srmacklem	}
3785191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3786214406Srmacklem	if (dp != NULL && (dp->nfsdl_flags &
3787214406Srmacklem	    (NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) ==
3788214406Srmacklem	     NFSCLDL_WRITE &&
3789191783Srmacklem	    (dp->nfsdl_sizelimit >= np->n_size ||
3790191783Srmacklem	     !NFSHASSTRICT3530(nmp))) {
3791191783Srmacklem		NFSUNLOCKCLSTATE();
3792210786Srmacklem		return (0);
3793191783Srmacklem	}
3794191783Srmacklem	NFSUNLOCKCLSTATE();
3795210786Srmacklem	return (1);
3796191783Srmacklem}
3797191783Srmacklem
3798191783Srmacklem/*
3799191783Srmacklem * See if a (write) delegation exists for this file.
3800191783Srmacklem */
3801191783SrmacklemAPPLESTATIC int
3802191783Srmacklemnfscl_nodeleg(vnode_t vp, int writedeleg)
3803191783Srmacklem{
3804191783Srmacklem	struct nfsclclient *clp;
3805191783Srmacklem	struct nfscldeleg *dp;
3806191783Srmacklem	struct nfsnode *np;
3807191783Srmacklem	struct nfsmount *nmp;
3808191783Srmacklem
3809191783Srmacklem	np = VTONFS(vp);
3810191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
3811191783Srmacklem	if (!NFSHASNFSV4(nmp))
3812191783Srmacklem		return (1);
3813191783Srmacklem	NFSLOCKCLSTATE();
3814191783Srmacklem	clp = nfscl_findcl(nmp);
3815191783Srmacklem	if (clp == NULL) {
3816191783Srmacklem		NFSUNLOCKCLSTATE();
3817191783Srmacklem		return (1);
3818191783Srmacklem	}
3819191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3820214406Srmacklem	if (dp != NULL &&
3821214406Srmacklem	    (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 &&
3822214406Srmacklem	    (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) ==
3823214406Srmacklem	     NFSCLDL_WRITE)) {
3824191783Srmacklem		NFSUNLOCKCLSTATE();
3825191783Srmacklem		return (0);
3826191783Srmacklem	}
3827191783Srmacklem	NFSUNLOCKCLSTATE();
3828191783Srmacklem	return (1);
3829191783Srmacklem}
3830191783Srmacklem
3831191783Srmacklem/*
3832191783Srmacklem * Look for an associated delegation that should be DelegReturned.
3833191783Srmacklem */
3834191783SrmacklemAPPLESTATIC int
3835191783Srmacklemnfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
3836191783Srmacklem{
3837191783Srmacklem	struct nfsclclient *clp;
3838191783Srmacklem	struct nfscldeleg *dp;
3839191783Srmacklem	struct nfsclowner *owp;
3840191783Srmacklem	struct nfscllockowner *lp;
3841191783Srmacklem	struct nfsmount *nmp;
3842191783Srmacklem	struct ucred *cred;
3843191783Srmacklem	struct nfsnode *np;
3844191783Srmacklem	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
3845191783Srmacklem
3846191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
3847191783Srmacklem	np = VTONFS(vp);
3848191783Srmacklem	NFSLOCKCLSTATE();
3849191783Srmacklem	/*
3850191783Srmacklem	 * Loop around waiting for:
3851191783Srmacklem	 * - outstanding I/O operations on delegations to complete
3852191783Srmacklem	 * - for a delegation on vp that has state, lock the client and
3853191783Srmacklem	 *   do a recall
3854191783Srmacklem	 * - return delegation with no state
3855191783Srmacklem	 */
3856191783Srmacklem	while (1) {
3857191783Srmacklem		clp = nfscl_findcl(nmp);
3858191783Srmacklem		if (clp == NULL) {
3859191783Srmacklem			NFSUNLOCKCLSTATE();
3860191783Srmacklem			return (retcnt);
3861191783Srmacklem		}
3862191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
3863191783Srmacklem		    np->n_fhp->nfh_len);
3864191783Srmacklem		if (dp != NULL) {
3865191783Srmacklem		    /*
3866191783Srmacklem		     * Wait for outstanding I/O ops to be done.
3867191783Srmacklem		     */
3868191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
3869191783Srmacklem			if (igotlock) {
3870191783Srmacklem			    nfsv4_unlock(&clp->nfsc_lock, 0);
3871191783Srmacklem			    igotlock = 0;
3872191783Srmacklem			}
3873191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
3874191783Srmacklem			(void) nfsmsleep(&dp->nfsdl_rwlock,
3875191783Srmacklem			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
3876191783Srmacklem			continue;
3877191783Srmacklem		    }
3878191783Srmacklem		    needsrecall = 0;
3879191783Srmacklem		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3880191783Srmacklem			if (!LIST_EMPTY(&owp->nfsow_open)) {
3881191783Srmacklem			    needsrecall = 1;
3882191783Srmacklem			    break;
3883191783Srmacklem			}
3884191783Srmacklem		    }
3885191783Srmacklem		    if (!needsrecall) {
3886191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3887191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
3888191783Srmacklem				needsrecall = 1;
3889191783Srmacklem				break;
3890191783Srmacklem			    }
3891191783Srmacklem			}
3892191783Srmacklem		    }
3893191783Srmacklem		    if (needsrecall && !triedrecall) {
3894214406Srmacklem			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
3895191783Srmacklem			islept = 0;
3896191783Srmacklem			while (!igotlock) {
3897191783Srmacklem			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
3898222389Srmacklem				&islept, NFSCLSTATEMUTEXPTR, NULL);
3899191783Srmacklem			    if (islept)
3900191783Srmacklem				break;
3901191783Srmacklem			}
3902191783Srmacklem			if (islept)
3903191783Srmacklem			    continue;
3904191783Srmacklem			NFSUNLOCKCLSTATE();
3905191783Srmacklem			cred = newnfs_getcred();
3906191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
3907207082Srmacklem			(void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0);
3908191783Srmacklem			NFSFREECRED(cred);
3909191783Srmacklem			triedrecall = 1;
3910191783Srmacklem			NFSLOCKCLSTATE();
3911191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
3912191783Srmacklem			igotlock = 0;
3913191783Srmacklem			continue;
3914191783Srmacklem		    }
3915191783Srmacklem		    *stp = dp->nfsdl_stateid;
3916191783Srmacklem		    retcnt = 1;
3917191783Srmacklem		    nfscl_cleandeleg(dp);
3918191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
3919191783Srmacklem		}
3920191783Srmacklem		if (igotlock)
3921191783Srmacklem		    nfsv4_unlock(&clp->nfsc_lock, 0);
3922191783Srmacklem		NFSUNLOCKCLSTATE();
3923191783Srmacklem		return (retcnt);
3924191783Srmacklem	}
3925191783Srmacklem}
3926191783Srmacklem
3927191783Srmacklem/*
3928191783Srmacklem * Look for associated delegation(s) that should be DelegReturned.
3929191783Srmacklem */
3930191783SrmacklemAPPLESTATIC int
3931191783Srmacklemnfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
3932191783Srmacklem    nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
3933191783Srmacklem{
3934191783Srmacklem	struct nfsclclient *clp;
3935191783Srmacklem	struct nfscldeleg *dp;
3936191783Srmacklem	struct nfsclowner *owp;
3937191783Srmacklem	struct nfscllockowner *lp;
3938191783Srmacklem	struct nfsmount *nmp;
3939191783Srmacklem	struct ucred *cred;
3940191783Srmacklem	struct nfsnode *np;
3941191783Srmacklem	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
3942191783Srmacklem
3943191783Srmacklem	nmp = VFSTONFS(vnode_mount(fvp));
3944191783Srmacklem	*gotfdp = 0;
3945191783Srmacklem	*gottdp = 0;
3946191783Srmacklem	NFSLOCKCLSTATE();
3947191783Srmacklem	/*
3948191783Srmacklem	 * Loop around waiting for:
3949191783Srmacklem	 * - outstanding I/O operations on delegations to complete
3950191783Srmacklem	 * - for a delegation on fvp that has state, lock the client and
3951191783Srmacklem	 *   do a recall
3952191783Srmacklem	 * - return delegation(s) with no state.
3953191783Srmacklem	 */
3954191783Srmacklem	while (1) {
3955191783Srmacklem		clp = nfscl_findcl(nmp);
3956191783Srmacklem		if (clp == NULL) {
3957191783Srmacklem			NFSUNLOCKCLSTATE();
3958191783Srmacklem			return (retcnt);
3959191783Srmacklem		}
3960191783Srmacklem		np = VTONFS(fvp);
3961191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
3962191783Srmacklem		    np->n_fhp->nfh_len);
3963191783Srmacklem		if (dp != NULL && *gotfdp == 0) {
3964191783Srmacklem		    /*
3965191783Srmacklem		     * Wait for outstanding I/O ops to be done.
3966191783Srmacklem		     */
3967191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
3968191783Srmacklem			if (igotlock) {
3969191783Srmacklem			    nfsv4_unlock(&clp->nfsc_lock, 0);
3970191783Srmacklem			    igotlock = 0;
3971191783Srmacklem			}
3972191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
3973191783Srmacklem			(void) nfsmsleep(&dp->nfsdl_rwlock,
3974191783Srmacklem			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
3975191783Srmacklem			continue;
3976191783Srmacklem		    }
3977191783Srmacklem		    needsrecall = 0;
3978191783Srmacklem		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3979191783Srmacklem			if (!LIST_EMPTY(&owp->nfsow_open)) {
3980191783Srmacklem			    needsrecall = 1;
3981191783Srmacklem			    break;
3982191783Srmacklem			}
3983191783Srmacklem		    }
3984191783Srmacklem		    if (!needsrecall) {
3985191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3986191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
3987191783Srmacklem				needsrecall = 1;
3988191783Srmacklem				break;
3989191783Srmacklem			    }
3990191783Srmacklem			}
3991191783Srmacklem		    }
3992191783Srmacklem		    if (needsrecall && !triedrecall) {
3993214406Srmacklem			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
3994191783Srmacklem			islept = 0;
3995191783Srmacklem			while (!igotlock) {
3996191783Srmacklem			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
3997222389Srmacklem				&islept, NFSCLSTATEMUTEXPTR, NULL);
3998191783Srmacklem			    if (islept)
3999191783Srmacklem				break;
4000191783Srmacklem			}
4001191783Srmacklem			if (islept)
4002191783Srmacklem			    continue;
4003191783Srmacklem			NFSUNLOCKCLSTATE();
4004191783Srmacklem			cred = newnfs_getcred();
4005191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
4006207082Srmacklem			(void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0);
4007191783Srmacklem			NFSFREECRED(cred);
4008191783Srmacklem			triedrecall = 1;
4009191783Srmacklem			NFSLOCKCLSTATE();
4010191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
4011191783Srmacklem			igotlock = 0;
4012191783Srmacklem			continue;
4013191783Srmacklem		    }
4014191783Srmacklem		    *fstp = dp->nfsdl_stateid;
4015191783Srmacklem		    retcnt++;
4016191783Srmacklem		    *gotfdp = 1;
4017191783Srmacklem		    nfscl_cleandeleg(dp);
4018191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4019191783Srmacklem		}
4020191783Srmacklem		if (igotlock) {
4021191783Srmacklem		    nfsv4_unlock(&clp->nfsc_lock, 0);
4022191783Srmacklem		    igotlock = 0;
4023191783Srmacklem		}
4024191783Srmacklem		if (tvp != NULL) {
4025191783Srmacklem		    np = VTONFS(tvp);
4026191783Srmacklem		    dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4027191783Srmacklem			np->n_fhp->nfh_len);
4028191783Srmacklem		    if (dp != NULL && *gottdp == 0) {
4029191783Srmacklem			/*
4030191783Srmacklem			 * Wait for outstanding I/O ops to be done.
4031191783Srmacklem			 */
4032191783Srmacklem			if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4033191783Srmacklem			    dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4034191783Srmacklem			    (void) nfsmsleep(&dp->nfsdl_rwlock,
4035191783Srmacklem				NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4036191783Srmacklem			    continue;
4037191783Srmacklem			}
4038191783Srmacklem			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4039191783Srmacklem			    if (!LIST_EMPTY(&owp->nfsow_open)) {
4040191783Srmacklem				NFSUNLOCKCLSTATE();
4041191783Srmacklem				return (retcnt);
4042191783Srmacklem			    }
4043191783Srmacklem			}
4044191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4045191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4046191783Srmacklem				NFSUNLOCKCLSTATE();
4047191783Srmacklem				return (retcnt);
4048191783Srmacklem			    }
4049191783Srmacklem			}
4050191783Srmacklem			*tstp = dp->nfsdl_stateid;
4051191783Srmacklem			retcnt++;
4052191783Srmacklem			*gottdp = 1;
4053191783Srmacklem			nfscl_cleandeleg(dp);
4054191783Srmacklem			nfscl_freedeleg(&clp->nfsc_deleg, dp);
4055191783Srmacklem		    }
4056191783Srmacklem		}
4057191783Srmacklem		NFSUNLOCKCLSTATE();
4058191783Srmacklem		return (retcnt);
4059191783Srmacklem	}
4060191783Srmacklem}
4061191783Srmacklem
4062191783Srmacklem/*
4063191783Srmacklem * Get a reference on the clientid associated with the mount point.
4064191783Srmacklem * Return 1 if success, 0 otherwise.
4065191783Srmacklem */
4066191783SrmacklemAPPLESTATIC int
4067191783Srmacklemnfscl_getref(struct nfsmount *nmp)
4068191783Srmacklem{
4069191783Srmacklem	struct nfsclclient *clp;
4070191783Srmacklem
4071191783Srmacklem	NFSLOCKCLSTATE();
4072191783Srmacklem	clp = nfscl_findcl(nmp);
4073191783Srmacklem	if (clp == NULL) {
4074191783Srmacklem		NFSUNLOCKCLSTATE();
4075191783Srmacklem		return (0);
4076191783Srmacklem	}
4077222389Srmacklem	nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL);
4078191783Srmacklem	NFSUNLOCKCLSTATE();
4079191783Srmacklem	return (1);
4080191783Srmacklem}
4081191783Srmacklem
4082191783Srmacklem/*
4083191783Srmacklem * Release a reference on a clientid acquired with the above call.
4084191783Srmacklem */
4085191783SrmacklemAPPLESTATIC void
4086191783Srmacklemnfscl_relref(struct nfsmount *nmp)
4087191783Srmacklem{
4088191783Srmacklem	struct nfsclclient *clp;
4089191783Srmacklem
4090191783Srmacklem	NFSLOCKCLSTATE();
4091191783Srmacklem	clp = nfscl_findcl(nmp);
4092191783Srmacklem	if (clp == NULL) {
4093191783Srmacklem		NFSUNLOCKCLSTATE();
4094191783Srmacklem		return;
4095191783Srmacklem	}
4096191783Srmacklem	nfsv4_relref(&clp->nfsc_lock);
4097191783Srmacklem	NFSUNLOCKCLSTATE();
4098191783Srmacklem}
4099191783Srmacklem
4100191783Srmacklem/*
4101191783Srmacklem * Save the size attribute in the delegation, since the nfsnode
4102191783Srmacklem * is going away.
4103191783Srmacklem */
4104191783SrmacklemAPPLESTATIC void
4105191783Srmacklemnfscl_reclaimnode(vnode_t vp)
4106191783Srmacklem{
4107191783Srmacklem	struct nfsclclient *clp;
4108191783Srmacklem	struct nfscldeleg *dp;
4109191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4110191783Srmacklem	struct nfsmount *nmp;
4111191783Srmacklem
4112191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4113191783Srmacklem	if (!NFSHASNFSV4(nmp))
4114191783Srmacklem		return;
4115191783Srmacklem	NFSLOCKCLSTATE();
4116191783Srmacklem	clp = nfscl_findcl(nmp);
4117191783Srmacklem	if (clp == NULL) {
4118191783Srmacklem		NFSUNLOCKCLSTATE();
4119191783Srmacklem		return;
4120191783Srmacklem	}
4121191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4122191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4123191783Srmacklem		dp->nfsdl_size = np->n_size;
4124191783Srmacklem	NFSUNLOCKCLSTATE();
4125191783Srmacklem}
4126191783Srmacklem
4127191783Srmacklem/*
4128191783Srmacklem * Get the saved size attribute in the delegation, since it is a
4129191783Srmacklem * newly allocated nfsnode.
4130191783Srmacklem */
4131191783SrmacklemAPPLESTATIC void
4132191783Srmacklemnfscl_newnode(vnode_t vp)
4133191783Srmacklem{
4134191783Srmacklem	struct nfsclclient *clp;
4135191783Srmacklem	struct nfscldeleg *dp;
4136191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4137191783Srmacklem	struct nfsmount *nmp;
4138191783Srmacklem
4139191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4140191783Srmacklem	if (!NFSHASNFSV4(nmp))
4141191783Srmacklem		return;
4142191783Srmacklem	NFSLOCKCLSTATE();
4143191783Srmacklem	clp = nfscl_findcl(nmp);
4144191783Srmacklem	if (clp == NULL) {
4145191783Srmacklem		NFSUNLOCKCLSTATE();
4146191783Srmacklem		return;
4147191783Srmacklem	}
4148191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4149191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4150191783Srmacklem		np->n_size = dp->nfsdl_size;
4151191783Srmacklem	NFSUNLOCKCLSTATE();
4152191783Srmacklem}
4153191783Srmacklem
4154191783Srmacklem/*
4155191783Srmacklem * If there is a valid write delegation for this file, set the modtime
4156191783Srmacklem * to the local clock time.
4157191783Srmacklem */
4158191783SrmacklemAPPLESTATIC void
4159191783Srmacklemnfscl_delegmodtime(vnode_t vp)
4160191783Srmacklem{
4161191783Srmacklem	struct nfsclclient *clp;
4162191783Srmacklem	struct nfscldeleg *dp;
4163191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4164191783Srmacklem	struct nfsmount *nmp;
4165191783Srmacklem
4166191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4167191783Srmacklem	if (!NFSHASNFSV4(nmp))
4168191783Srmacklem		return;
4169191783Srmacklem	NFSLOCKCLSTATE();
4170191783Srmacklem	clp = nfscl_findcl(nmp);
4171191783Srmacklem	if (clp == NULL) {
4172191783Srmacklem		NFSUNLOCKCLSTATE();
4173191783Srmacklem		return;
4174191783Srmacklem	}
4175191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4176191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
4177247502Sjhb		nanotime(&dp->nfsdl_modtime);
4178191783Srmacklem		dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
4179191783Srmacklem	}
4180191783Srmacklem	NFSUNLOCKCLSTATE();
4181191783Srmacklem}
4182191783Srmacklem
4183191783Srmacklem/*
4184191783Srmacklem * If there is a valid write delegation for this file with a modtime set,
4185191783Srmacklem * put that modtime in mtime.
4186191783Srmacklem */
4187191783SrmacklemAPPLESTATIC void
4188191783Srmacklemnfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
4189191783Srmacklem{
4190191783Srmacklem	struct nfsclclient *clp;
4191191783Srmacklem	struct nfscldeleg *dp;
4192191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4193191783Srmacklem	struct nfsmount *nmp;
4194191783Srmacklem
4195191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4196191783Srmacklem	if (!NFSHASNFSV4(nmp))
4197191783Srmacklem		return;
4198191783Srmacklem	NFSLOCKCLSTATE();
4199191783Srmacklem	clp = nfscl_findcl(nmp);
4200191783Srmacklem	if (clp == NULL) {
4201191783Srmacklem		NFSUNLOCKCLSTATE();
4202191783Srmacklem		return;
4203191783Srmacklem	}
4204191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4205191783Srmacklem	if (dp != NULL &&
4206191783Srmacklem	    (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
4207191783Srmacklem	    (NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
4208191783Srmacklem		*mtime = dp->nfsdl_modtime;
4209191783Srmacklem	NFSUNLOCKCLSTATE();
4210191783Srmacklem}
4211191783Srmacklem
4212191783Srmacklemstatic int
4213191783Srmacklemnfscl_errmap(struct nfsrv_descript *nd)
4214191783Srmacklem{
4215191783Srmacklem	short *defaulterrp, *errp;
4216191783Srmacklem
4217191783Srmacklem	if (!nd->nd_repstat)
4218191783Srmacklem		return (0);
4219191783Srmacklem	if (nd->nd_procnum == NFSPROC_NOOP)
4220191783Srmacklem		return (txdr_unsigned(nd->nd_repstat & 0xffff));
4221191783Srmacklem	if (nd->nd_repstat == EBADRPC)
4222191783Srmacklem		return (txdr_unsigned(NFSERR_BADXDR));
4223191783Srmacklem	if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
4224191783Srmacklem	    nd->nd_repstat == NFSERR_OPILLEGAL)
4225191783Srmacklem		return (txdr_unsigned(nd->nd_repstat));
4226191783Srmacklem	errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
4227191783Srmacklem	while (*++errp)
4228191783Srmacklem		if (*errp == (short)nd->nd_repstat)
4229191783Srmacklem			return (txdr_unsigned(nd->nd_repstat));
4230191783Srmacklem	return (txdr_unsigned(*defaulterrp));
4231191783Srmacklem}
4232191783Srmacklem
4233