nfs_clstate.c revision 317423
1178479Sjb/*-
2178479Sjb * Copyright (c) 2009 Rick Macklem, University of Guelph
3178479Sjb * All rights reserved.
4178479Sjb *
5178479Sjb * Redistribution and use in source and binary forms, with or without
6178479Sjb * modification, are permitted provided that the following conditions
7178479Sjb * are met:
8178479Sjb * 1. Redistributions of source code must retain the above copyright
9178479Sjb *    notice, this list of conditions and the following disclaimer.
10178479Sjb * 2. Redistributions in binary form must reproduce the above copyright
11178479Sjb *    notice, this list of conditions and the following disclaimer in the
12178479Sjb *    documentation and/or other materials provided with the distribution.
13178479Sjb *
14178479Sjb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15178479Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16178479Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17178479Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18178479Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19178479Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20178479Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21210767Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22178479Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23210767Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24178479Sjb * SUCH DAMAGE.
25178479Sjb *
26178479Sjb */
27178559Sjb
28178479Sjb#include <sys/cdefs.h>
29178479Sjb__FBSDID("$FreeBSD: stable/10/sys/fs/nfsclient/nfs_clstate.c 317423 2017-04-25 20:34:56Z rmacklem $");
30178479Sjb
31178479Sjb/*
32178479Sjb * These functions implement the client side state handling for NFSv4.
33178479Sjb * NFSv4 state handling:
34178559Sjb * - A lockowner is used to determine lock contention, so it
35178559Sjb *   corresponds directly to a Posix pid. (1 to 1 mapping)
36178559Sjb * - The correct granularity of an OpenOwner is not nearly so
37178559Sjb *   obvious. An OpenOwner does the following:
38178559Sjb *   - provides a serial sequencing of Open/Close/Lock-with-new-lockowner
39178479Sjb *   - is used to check for Open/Share contention (not applicable to
40178479Sjb *     this client, since all Opens are Deny_None)
41178559Sjb *   As such, I considered both extreme.
42178479Sjb *   1 OpenOwner per ClientID - Simple to manage, but fully serializes
43178559Sjb *   all Open, Close and Lock (with a new lockowner) Ops.
44178479Sjb *   1 OpenOwner for each Open - This one results in an OpenConfirm for
45178479Sjb *   every Open, for most servers.
46178479Sjb *   So, I chose to use the same mapping as I did for LockOwnwers.
47178479Sjb *   The main concern here is that you can end up with multiple Opens
48178479Sjb *   for the same File Handle, but on different OpenOwners (opens
49178479Sjb *   inherited from parents, grandparents...) and you do not know
50178479Sjb *   which of these the vnodeop close applies to. This is handled by
51178559Sjb *   delaying the Close Op(s) until all of the Opens have been closed.
52178559Sjb *   (It is not yet obvious if this is the correct granularity.)
53178559Sjb * - How the code handles serialization:
54178479Sjb *   - For the ClientId, it uses an exclusive lock while getting its
55178479Sjb *     SetClientId and during recovery. Otherwise, it uses a shared
56178479Sjb *     lock via a reference count.
57178479Sjb *   - For the rest of the data structures, it uses an SMP mutex
58178479Sjb *     (once the nfs client is SMP safe) and doesn't sleep while
59178479Sjb *     manipulating the linked lists.
60178479Sjb *   - The serialization of Open/Close/Lock/LockU falls out in the
61178479Sjb *     "wash", since OpenOwners and LockOwners are both mapped from
62178479Sjb *     Posix pid. In other words, there is only one Posix pid using
63178479Sjb *     any given owner, so that owner is serialized. (If you change
64178479Sjb *     the granularity of the OpenOwner, then code must be added to
65178479Sjb *     serialize Ops on the OpenOwner.)
66178479Sjb * - When to get rid of OpenOwners and LockOwners.
67178479Sjb *   - The function nfscl_cleanup_common() is executed after a process exits.
68178479Sjb *     It goes through the client list looking for all Open and Lock Owners.
69178479Sjb *     When one is found, it is marked "defunct" or in the case of
70178479Sjb *     an OpenOwner without any Opens, freed.
71178479Sjb *     The renew thread scans for defunct Owners and gets rid of them,
72178479Sjb *     if it can. The LockOwners will also be deleted when the
73178479Sjb *     associated Open is closed.
74178479Sjb *   - If the LockU or Close Op(s) fail during close in a way
75178479Sjb *     that could be recovered upon retry, they are relinked to the
76178479Sjb *     ClientId's defunct open list and retried by the renew thread
77178479Sjb *     until they succeed or an unmount/recovery occurs.
78210767Srpaulo *     (Since we are done with them, they do not need to be recovered.)
79210767Srpaulo */
80210767Srpaulo
81210767Srpaulo#ifndef APPLEKEXT
82178559Sjb#include <fs/nfs/nfsport.h>
83178479Sjb
84178479Sjb/*
85178479Sjb * Global variables
86178479Sjb */
87178479Sjbextern struct nfsstats newnfsstats;
88210425Savgextern struct nfsreqhead nfsd_reqq;
89210425Savgextern u_int32_t newnfs_false, newnfs_true;
90210425Savgextern int nfscl_debuglevel;
91210425SavgNFSREQSPINLOCK;
92210425SavgNFSCLSTATEMUTEX;
93210425Savgint nfscl_inited = 0;
94210425Savgstruct nfsclhead nfsclhead;	/* Head of clientid list */
95210425Savgint nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
96178479Sjbint nfscl_layouthighwater = NFSCLLAYOUTHIGHWATER;
97178479Sjb#endif	/* !APPLEKEXT */
98178479Sjb
99178479Sjbstatic int nfscl_delegcnt = 0;
100178479Sjbstatic int nfscl_layoutcnt = 0;
101178479Sjbstatic int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *,
102178479Sjb    u_int8_t *, u_int32_t, struct nfscllockowner **, struct nfsclopen **);
103178479Sjbstatic void nfscl_clrelease(struct nfsclclient *);
104178479Sjbstatic void nfscl_cleanclient(struct nfsclclient *);
105178479Sjbstatic void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
106178479Sjb    struct ucred *, NFSPROC_T *);
107178559Sjbstatic int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
108178479Sjb    struct nfsmount *, struct ucred *, NFSPROC_T *);
109178479Sjbstatic void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *);
110210425Savgstatic void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
111178559Sjb    struct nfscllock *, int);
112210425Savgstatic int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
113210425Savg    struct nfscllock **, int);
114210425Savgstatic void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *);
115210425Savgstatic u_int32_t nfscl_nextcbident(void);
116178559Sjbstatic mount_t nfscl_getmnt(int, uint8_t *, u_int32_t, struct nfsclclient **);
117178559Sjbstatic struct nfsclclient *nfscl_getclnt(u_int32_t);
118178559Sjbstatic struct nfsclclient *nfscl_getclntsess(uint8_t *);
119178479Sjbstatic struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *,
120178479Sjb    int);
121178479Sjbstatic void nfscl_retoncloselayout(struct nfsclclient *, uint8_t *, int);
122178479Sjbstatic void nfscl_reldevinfo_locked(struct nfscldevinfo *);
123178479Sjbstatic struct nfscllayout *nfscl_findlayout(struct nfsclclient *, u_int8_t *,
124178479Sjb    int);
125178479Sjbstatic struct nfscldevinfo *nfscl_finddevinfo(struct nfsclclient *, uint8_t *);
126178479Sjbstatic int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *,
127178479Sjb    u_int8_t *, struct nfscllock **);
128210767Srpaulostatic void nfscl_freealllocks(struct nfscllockownerhead *, int);
129210767Srpaulostatic int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int,
130210767Srpaulo    struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **);
131210767Srpaulostatic void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *,
132178559Sjb    struct nfsclowner **, struct nfsclowner **, struct nfsclopen **,
133178479Sjb    struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *);
134178479Sjbstatic int nfscl_moveopen(vnode_t , struct nfsclclient *,
135178479Sjb    struct nfsmount *, struct nfsclopen *, struct nfsclowner *,
136178479Sjb    struct nfscldeleg *, struct ucred *, NFSPROC_T *);
137178479Sjbstatic void nfscl_totalrecall(struct nfsclclient *);
138210425Savgstatic int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *,
139210425Savg    struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *);
140210425Savgstatic int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int,
141210425Savg    u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int,
142210425Savg    struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *);
143210425Savgstatic int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
144210425Savg    int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short,
145210425Savg    struct ucred *, NFSPROC_T *);
146178479Sjbstatic int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
147178479Sjb    struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
148178479Sjbstatic void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *);
149178479Sjbstatic int nfscl_errmap(struct nfsrv_descript *, u_int32_t);
150178479Sjbstatic void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
151178479Sjbstatic int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
152178479Sjb    struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int);
153178479Sjbstatic void nfscl_freeopenowner(struct nfsclowner *, int);
154178479Sjbstatic void nfscl_cleandeleg(struct nfscldeleg *);
155178479Sjbstatic int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
156178479Sjb    struct nfsmount *, NFSPROC_T *);
157178559Sjbstatic void nfscl_emptylockowner(struct nfscllockowner *,
158178479Sjb    struct nfscllockownerfhhead *);
159210425Savgstatic void nfscl_mergeflayouts(struct nfsclflayouthead *,
160178559Sjb    struct nfsclflayouthead *);
161210425Savgstatic int nfscl_layoutrecall(int, struct nfscllayout *, uint32_t, uint64_t,
162210425Savg    uint64_t, uint32_t, struct nfsclrecalllayout *);
163210425Savgstatic int nfscl_seq(uint32_t, uint32_t);
164210425Savgstatic void nfscl_layoutreturn(struct nfsmount *, struct nfscllayout *,
165178559Sjb    struct ucred *, NFSPROC_T *);
166178559Sjbstatic void nfscl_dolayoutcommit(struct nfsmount *, struct nfscllayout *,
167178559Sjb    struct ucred *, NFSPROC_T *);
168178479Sjb
169178479Sjbstatic short nfscberr_null[] = {
170178479Sjb	0,
171178479Sjb	0,
172178479Sjb};
173178479Sjb
174178479Sjbstatic short nfscberr_getattr[] = {
175178479Sjb	NFSERR_RESOURCE,
176178479Sjb	NFSERR_BADHANDLE,
177178479Sjb	NFSERR_BADXDR,
178178479Sjb	NFSERR_RESOURCE,
179178479Sjb	NFSERR_SERVERFAULT,
180178479Sjb	0,
181178479Sjb};
182178479Sjb
183178479Sjbstatic short nfscberr_recall[] = {
184178479Sjb	NFSERR_RESOURCE,
185178479Sjb	NFSERR_BADHANDLE,
186178479Sjb	NFSERR_BADSTATEID,
187178479Sjb	NFSERR_BADXDR,
188178479Sjb	NFSERR_RESOURCE,
189178479Sjb	NFSERR_SERVERFAULT,
190178479Sjb	0,
191178479Sjb};
192178479Sjb
193178479Sjbstatic short *nfscl_cberrmap[] = {
194178479Sjb	nfscberr_null,
195178479Sjb	nfscberr_null,
196178479Sjb	nfscberr_null,
197178479Sjb	nfscberr_getattr,
198178479Sjb	nfscberr_recall
199178479Sjb};
200178479Sjb
201178479Sjb#define	NETFAMILY(clp) \
202178479Sjb		(((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET)
203178479Sjb
204178479Sjb/*
205178479Sjb * Called for an open operation.
206178479Sjb * If the nfhp argument is NULL, just get an openowner.
207178479Sjb */
208178479SjbAPPLESTATIC int
209178479Sjbnfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg,
210178479Sjb    struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp,
211178479Sjb    struct nfsclopen **opp, int *newonep, int *retp, int lockit)
212178479Sjb{
213178479Sjb	struct nfsclclient *clp;
214178479Sjb	struct nfsclowner *owp, *nowp;
215178479Sjb	struct nfsclopen *op = NULL, *nop = NULL;
216178479Sjb	struct nfscldeleg *dp;
217178479Sjb	struct nfsclownerhead *ohp;
218178479Sjb	u_int8_t own[NFSV4CL_LOCKNAMELEN];
219178479Sjb	int ret;
220178479Sjb
221178479Sjb	if (newonep != NULL)
222178479Sjb		*newonep = 0;
223178479Sjb	if (opp != NULL)
224178479Sjb		*opp = NULL;
225178479Sjb	if (owpp != NULL)
226178479Sjb		*owpp = NULL;
227178479Sjb
228178479Sjb	/*
229178479Sjb	 * Might need one or both of these, so MALLOC them now, to
230178479Sjb	 * avoid a tsleep() in MALLOC later.
231178479Sjb	 */
232178479Sjb	MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner),
233178479Sjb	    M_NFSCLOWNER, M_WAITOK);
234178479Sjb	if (nfhp != NULL)
235178479Sjb	    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
236178479Sjb		fhlen - 1, M_NFSCLOPEN, M_WAITOK);
237178479Sjb	ret = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
238178479Sjb	if (ret != 0) {
239178479Sjb		FREE((caddr_t)nowp, M_NFSCLOWNER);
240178479Sjb		if (nop != NULL)
241178479Sjb			FREE((caddr_t)nop, M_NFSCLOPEN);
242178479Sjb		return (ret);
243178479Sjb	}
244178479Sjb
245178479Sjb	/*
246178479Sjb	 * Get the Open iff it already exists.
247178479Sjb	 * If none found, add the new one or return error, depending upon
248178479Sjb	 * "create".
249178479Sjb	 */
250178479Sjb	nfscl_filllockowner(p->td_proc, own, F_POSIX);
251178479Sjb	NFSLOCKCLSTATE();
252178479Sjb	dp = NULL;
253178479Sjb	/* First check the delegation list */
254178479Sjb	if (nfhp != NULL && usedeleg) {
255178479Sjb		LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
256178479Sjb			if (dp->nfsdl_fhlen == fhlen &&
257178479Sjb			    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
258178479Sjb				if (!(amode & NFSV4OPEN_ACCESSWRITE) ||
259178479Sjb				    (dp->nfsdl_flags & NFSCLDL_WRITE))
260178479Sjb					break;
261178479Sjb				dp = NULL;
262178479Sjb				break;
263178479Sjb			}
264178479Sjb		}
265178479Sjb	}
266178479Sjb
267178479Sjb	if (dp != NULL)
268178479Sjb		ohp = &dp->nfsdl_owner;
269178479Sjb	else
270178479Sjb		ohp = &clp->nfsc_owner;
271178479Sjb	/* Now, search for an openowner */
272178479Sjb	LIST_FOREACH(owp, ohp, nfsow_list) {
273178479Sjb		if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN))
274178479Sjb			break;
275178479Sjb	}
276178479Sjb
277178479Sjb	/*
278178479Sjb	 * Create a new open, as required.
279178479Sjb	 */
280178479Sjb	nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen,
281178479Sjb	    newonep);
282178479Sjb
283178479Sjb	/*
284178479Sjb	 * Now, check the mode on the open and return the appropriate
285178479Sjb	 * value.
286178479Sjb	 */
287178479Sjb	if (retp != NULL) {
288178479Sjb		if (nfhp != NULL && dp != NULL && nop == NULL)
289178479Sjb			/* new local open on delegation */
290178479Sjb			*retp = NFSCLOPEN_SETCRED;
291178479Sjb		else
292178479Sjb			*retp = NFSCLOPEN_OK;
293178479Sjb	}
294178479Sjb	if (op != NULL && (amode & ~(op->nfso_mode))) {
295178479Sjb		op->nfso_mode |= amode;
296178479Sjb		if (retp != NULL && dp == NULL)
297178479Sjb			*retp = NFSCLOPEN_DOOPEN;
298178479Sjb	}
299178479Sjb
300178479Sjb	/*
301178479Sjb	 * Serialize modifications to the open owner for multiple threads
302178479Sjb	 * within the same process using a read/write sleep lock.
303178479Sjb	 */
304178479Sjb	if (lockit)
305178479Sjb		nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
306178479Sjb	NFSUNLOCKCLSTATE();
307178479Sjb	if (nowp != NULL)
308178479Sjb		FREE((caddr_t)nowp, M_NFSCLOWNER);
309178479Sjb	if (nop != NULL)
310178479Sjb		FREE((caddr_t)nop, M_NFSCLOPEN);
311178479Sjb	if (owpp != NULL)
312178479Sjb		*owpp = owp;
313178479Sjb	if (opp != NULL)
314178479Sjb		*opp = op;
315178479Sjb	return (0);
316178479Sjb}
317178479Sjb
318178479Sjb/*
319178479Sjb * Create a new open, as required.
320178479Sjb */
321178479Sjbstatic void
322178479Sjbnfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp,
323178479Sjb    struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp,
324178479Sjb    struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen,
325178479Sjb    int *newonep)
326178479Sjb{
327178479Sjb	struct nfsclowner *owp = *owpp, *nowp;
328178479Sjb	struct nfsclopen *op, *nop;
329178479Sjb
330178479Sjb	if (nowpp != NULL)
331178479Sjb		nowp = *nowpp;
332178479Sjb	else
333178479Sjb		nowp = NULL;
334178479Sjb	if (nopp != NULL)
335178479Sjb		nop = *nopp;
336178479Sjb	else
337178479Sjb		nop = NULL;
338178479Sjb	if (owp == NULL && nowp != NULL) {
339178479Sjb		NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
340178479Sjb		LIST_INIT(&nowp->nfsow_open);
341178479Sjb		nowp->nfsow_clp = clp;
342178479Sjb		nowp->nfsow_seqid = 0;
343178479Sjb		nowp->nfsow_defunct = 0;
344178479Sjb		nfscl_lockinit(&nowp->nfsow_rwlock);
345178479Sjb		if (dp != NULL) {
346178479Sjb			newnfsstats.cllocalopenowners++;
347178479Sjb			LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list);
348178479Sjb		} else {
349178479Sjb			newnfsstats.clopenowners++;
350178479Sjb			LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list);
351178479Sjb		}
352178479Sjb		owp = *owpp = nowp;
353178479Sjb		*nowpp = NULL;
354178479Sjb		if (newonep != NULL)
355178479Sjb			*newonep = 1;
356178479Sjb	}
357178479Sjb
358178479Sjb	 /* If an fhp has been specified, create an Open as well. */
359178479Sjb	if (fhp != NULL) {
360178479Sjb		/* and look for the correct open, based upon FH */
361178479Sjb		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
362178479Sjb			if (op->nfso_fhlen == fhlen &&
363178479Sjb			    !NFSBCMP(op->nfso_fh, fhp, fhlen))
364178479Sjb				break;
365178479Sjb		}
366178479Sjb		if (op == NULL && nop != NULL) {
367178479Sjb			nop->nfso_own = owp;
368178479Sjb			nop->nfso_mode = 0;
369178479Sjb			nop->nfso_opencnt = 0;
370178479Sjb			nop->nfso_posixlock = 1;
371178479Sjb			nop->nfso_fhlen = fhlen;
372178479Sjb			NFSBCOPY(fhp, nop->nfso_fh, fhlen);
373178479Sjb			LIST_INIT(&nop->nfso_lock);
374178479Sjb			nop->nfso_stateid.seqid = 0;
375178479Sjb			nop->nfso_stateid.other[0] = 0;
376178479Sjb			nop->nfso_stateid.other[1] = 0;
377178479Sjb			nop->nfso_stateid.other[2] = 0;
378178479Sjb			if (dp != NULL) {
379178479Sjb				TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
380178479Sjb				TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
381178479Sjb				    nfsdl_list);
382178479Sjb				dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
383178479Sjb				newnfsstats.cllocalopens++;
384178479Sjb			} else {
385178479Sjb				newnfsstats.clopens++;
386178479Sjb			}
387178479Sjb			LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list);
388178479Sjb			*opp = nop;
389178479Sjb			*nopp = NULL;
390178479Sjb			if (newonep != NULL)
391178479Sjb				*newonep = 1;
392178479Sjb		} else {
393178479Sjb			*opp = op;
394178479Sjb		}
395178479Sjb	}
396178479Sjb}
397178479Sjb
398178479Sjb/*
399178479Sjb * Called to find/add a delegation to a client.
400178479Sjb */
401178479SjbAPPLESTATIC int
402178479Sjbnfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
403178479Sjb    int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp)
404178479Sjb{
405178479Sjb	struct nfscldeleg *dp = *dpp, *tdp;
406178479Sjb
407178479Sjb	/*
408178479Sjb	 * First, if we have received a Read delegation for a file on a
409178479Sjb	 * read/write file system, just return it, because they aren't
410178479Sjb	 * useful, imho.
411178479Sjb	 */
412178479Sjb	if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) &&
413178479Sjb	    (dp->nfsdl_flags & NFSCLDL_READ)) {
414178479Sjb		(void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p);
415178479Sjb		FREE((caddr_t)dp, M_NFSCLDELEG);
416178479Sjb		*dpp = NULL;
417178479Sjb		return (0);
418178479Sjb	}
419178479Sjb
420178479Sjb	/* Look for the correct deleg, based upon FH */
421178479Sjb	NFSLOCKCLSTATE();
422178479Sjb	tdp = nfscl_finddeleg(clp, nfhp, fhlen);
423178479Sjb	if (tdp == NULL) {
424178479Sjb		if (dp == NULL) {
425178479Sjb			NFSUNLOCKCLSTATE();
426178479Sjb			return (NFSERR_BADSTATEID);
427178479Sjb		}
428178479Sjb		*dpp = NULL;
429178479Sjb		TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
430178479Sjb		LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp,
431178479Sjb		    nfsdl_hash);
432178479Sjb		dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
433178479Sjb		newnfsstats.cldelegates++;
434178479Sjb		nfscl_delegcnt++;
435178479Sjb	} else {
436178479Sjb		/*
437178479Sjb		 * Delegation already exists, what do we do if a new one??
438178479Sjb		 */
439178479Sjb		if (dp != NULL) {
440178479Sjb			printf("Deleg already exists!\n");
441178479Sjb			FREE((caddr_t)dp, M_NFSCLDELEG);
442178479Sjb			*dpp = NULL;
443178479Sjb		} else {
444178479Sjb			*dpp = tdp;
445178479Sjb		}
446178479Sjb	}
447178479Sjb	NFSUNLOCKCLSTATE();
448178479Sjb	return (0);
449178479Sjb}
450178479Sjb
451178479Sjb/*
452178479Sjb * Find a delegation for this file handle. Return NULL upon failure.
453178479Sjb */
454178479Sjbstatic struct nfscldeleg *
455178479Sjbnfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
456178479Sjb{
457178479Sjb	struct nfscldeleg *dp;
458178479Sjb
459178479Sjb	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) {
460178479Sjb	    if (dp->nfsdl_fhlen == fhlen &&
461178479Sjb		!NFSBCMP(dp->nfsdl_fh, fhp, fhlen))
462178479Sjb		break;
463178479Sjb	}
464178479Sjb	return (dp);
465178479Sjb}
466178479Sjb
467178479Sjb/*
468178479Sjb * Get a stateid for an I/O operation. First, look for an open and iff
469178479Sjb * found, return either a lockowner stateid or the open stateid.
470178479Sjb * If no Open is found, just return error and the special stateid of all zeros.
471178479Sjb */
472178479SjbAPPLESTATIC int
473178479Sjbnfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
474178479Sjb    int fords, struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp,
475178479Sjb    void **lckpp)
476178479Sjb{
477178479Sjb	struct nfsclclient *clp;
478178479Sjb	struct nfsclowner *owp;
479178479Sjb	struct nfsclopen *op = NULL;
480178479Sjb	struct nfscllockowner *lp;
481178479Sjb	struct nfscldeleg *dp;
482178479Sjb	struct nfsnode *np;
483178479Sjb	u_int8_t own[NFSV4CL_LOCKNAMELEN];
484178479Sjb	int error, done;
485178479Sjb
486178479Sjb	*lckpp = NULL;
487178479Sjb	/*
488178479Sjb	 * Initially, just set the special stateid of all zeros.
489178479Sjb	 * (Don't do this for a DS, since the special stateid can't be used.)
490178479Sjb	 */
491178479Sjb	if (fords == 0) {
492178479Sjb		stateidp->seqid = 0;
493178479Sjb		stateidp->other[0] = 0;
494178479Sjb		stateidp->other[1] = 0;
495178479Sjb		stateidp->other[2] = 0;
496178479Sjb	}
497178479Sjb	if (vnode_vtype(vp) != VREG)
498178479Sjb		return (EISDIR);
499178479Sjb	np = VTONFS(vp);
500178479Sjb	NFSLOCKCLSTATE();
501178479Sjb	clp = nfscl_findcl(VFSTONFS(vnode_mount(vp)));
502178479Sjb	if (clp == NULL) {
503178479Sjb		NFSUNLOCKCLSTATE();
504178479Sjb		return (EACCES);
505178479Sjb	}
506178479Sjb
507178479Sjb	/*
508178479Sjb	 * Wait for recovery to complete.
509178479Sjb	 */
510178479Sjb	while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG))
511178479Sjb		(void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR,
512178479Sjb		    PZERO, "nfsrecvr", NULL);
513178479Sjb
514178479Sjb	/*
515178479Sjb	 * First, look for a delegation.
516178479Sjb	 */
517178479Sjb	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
518178479Sjb		if (dp->nfsdl_fhlen == fhlen &&
519178479Sjb		    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
520178479Sjb			if (!(mode & NFSV4OPEN_ACCESSWRITE) ||
521210767Srpaulo			    (dp->nfsdl_flags & NFSCLDL_WRITE)) {
522178479Sjb				stateidp->seqid = dp->nfsdl_stateid.seqid;
523178479Sjb				stateidp->other[0] = dp->nfsdl_stateid.other[0];
524178479Sjb				stateidp->other[1] = dp->nfsdl_stateid.other[1];
525178479Sjb				stateidp->other[2] = dp->nfsdl_stateid.other[2];
526178479Sjb				if (!(np->n_flag & NDELEGRECALL)) {
527178479Sjb					TAILQ_REMOVE(&clp->nfsc_deleg, dp,
528178479Sjb					    nfsdl_list);
529178479Sjb					TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
530178479Sjb					    nfsdl_list);
531178479Sjb					dp->nfsdl_timestamp = NFSD_MONOSEC +
532178479Sjb					    120;
533178479Sjb					dp->nfsdl_rwlock.nfslock_usecnt++;
534178479Sjb					*lckpp = (void *)&dp->nfsdl_rwlock;
535178479Sjb				}
536178479Sjb				NFSUNLOCKCLSTATE();
537178479Sjb				return (0);
538178479Sjb			}
539178479Sjb			break;
540178479Sjb		}
541178479Sjb	}
542178559Sjb
543178479Sjb	if (p != NULL) {
544178559Sjb		/*
545178559Sjb		 * If p != NULL, we want to search the parentage tree
546178559Sjb		 * for a matching OpenOwner and use that.
547178559Sjb		 */
548178559Sjb		nfscl_filllockowner(p->td_proc, own, F_POSIX);
549178479Sjb		lp = NULL;
550178479Sjb		error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, own, own,
551178479Sjb		    mode, &lp, &op);
552178479Sjb		if (error == 0 && lp != NULL && fords == 0) {
553178479Sjb			/* Don't return a lock stateid for a DS. */
554178479Sjb			stateidp->seqid =
555178479Sjb			    lp->nfsl_stateid.seqid;
556178479Sjb			stateidp->other[0] =
557178479Sjb			    lp->nfsl_stateid.other[0];
558178479Sjb			stateidp->other[1] =
559178479Sjb			    lp->nfsl_stateid.other[1];
560178479Sjb			stateidp->other[2] =
561178479Sjb			    lp->nfsl_stateid.other[2];
562178479Sjb			NFSUNLOCKCLSTATE();
563178479Sjb			return (0);
564178479Sjb		}
565178479Sjb	}
566178479Sjb	if (op == NULL) {
567178479Sjb		/* If not found, just look for any OpenOwner that will work. */
568178479Sjb		done = 0;
569178479Sjb		owp = LIST_FIRST(&clp->nfsc_owner);
570178479Sjb		while (!done && owp != NULL) {
571178479Sjb			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
572178479Sjb				if (op->nfso_fhlen == fhlen &&
573178479Sjb				    !NFSBCMP(op->nfso_fh, nfhp, fhlen) &&
574178479Sjb				    (mode & op->nfso_mode) == mode) {
575178479Sjb					done = 1;
576178479Sjb					break;
577178479Sjb				}
578178479Sjb			}
579178479Sjb			if (!done)
580178479Sjb				owp = LIST_NEXT(owp, nfsow_list);
581178479Sjb		}
582178479Sjb		if (!done) {
583178479Sjb			NFSUNLOCKCLSTATE();
584178479Sjb			return (ENOENT);
585178479Sjb		}
586178479Sjb		/*
587178479Sjb		 * For read aheads or write behinds, use the open cred.
588178479Sjb		 * A read ahead or write behind is indicated by p == NULL.
589178479Sjb		 */
590178479Sjb		if (p == NULL)
591178479Sjb			newnfs_copycred(&op->nfso_cred, cred);
592178479Sjb	}
593178479Sjb
594178479Sjb	/*
595178479Sjb	 * No lock stateid, so return the open stateid.
596178479Sjb	 */
597178479Sjb	stateidp->seqid = op->nfso_stateid.seqid;
598178479Sjb	stateidp->other[0] = op->nfso_stateid.other[0];
599178479Sjb	stateidp->other[1] = op->nfso_stateid.other[1];
600178479Sjb	stateidp->other[2] = op->nfso_stateid.other[2];
601178479Sjb	NFSUNLOCKCLSTATE();
602178479Sjb	return (0);
603178479Sjb}
604178479Sjb
605178479Sjb/*
606178479Sjb * Search for a matching file, mode and, optionally, lockowner.
607178479Sjb */
608178479Sjbstatic int
609178479Sjbnfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen,
610178479Sjb    u_int8_t *openown, u_int8_t *lockown, u_int32_t mode,
611178479Sjb    struct nfscllockowner **lpp, struct nfsclopen **opp)
612178479Sjb{
613178479Sjb	struct nfsclowner *owp;
614178479Sjb	struct nfsclopen *op, *rop, *rop2;
615178479Sjb	struct nfscllockowner *lp;
616178479Sjb	int keep_looping;
617178479Sjb
618178479Sjb	if (lpp != NULL)
619178479Sjb		*lpp = NULL;
620178479Sjb	/*
621178479Sjb	 * rop will be set to the open to be returned. There are three
622178479Sjb	 * variants of this, all for an open of the correct file:
623178479Sjb	 * 1 - A match of lockown.
624178479Sjb	 * 2 - A match of the openown, when no lockown match exists.
625178479Sjb	 * 3 - A match for any open, if no openown or lockown match exists.
626178479Sjb	 * Looking for #2 over #3 probably isn't necessary, but since
627178479Sjb	 * RFC3530 is vague w.r.t. the relationship between openowners and
628178479Sjb	 * lockowners, I think this is the safer way to go.
629178479Sjb	 */
630178479Sjb	rop = NULL;
631178479Sjb	rop2 = NULL;
632178479Sjb	keep_looping = 1;
633178479Sjb	/* Search the client list */
634178479Sjb	owp = LIST_FIRST(ohp);
635178479Sjb	while (owp != NULL && keep_looping != 0) {
636178479Sjb		/* and look for the correct open */
637178479Sjb		op = LIST_FIRST(&owp->nfsow_open);
638178479Sjb		while (op != NULL && keep_looping != 0) {
639178479Sjb			if (op->nfso_fhlen == fhlen &&
640178479Sjb			    !NFSBCMP(op->nfso_fh, nfhp, fhlen)
641178479Sjb			    && (op->nfso_mode & mode) == mode) {
642178479Sjb				if (lpp != NULL) {
643178479Sjb					/* Now look for a matching lockowner. */
644178479Sjb					LIST_FOREACH(lp, &op->nfso_lock,
645178479Sjb					    nfsl_list) {
646178479Sjb						if (!NFSBCMP(lp->nfsl_owner,
647178479Sjb						    lockown,
648178479Sjb						    NFSV4CL_LOCKNAMELEN)) {
649178479Sjb							*lpp = lp;
650178479Sjb							rop = op;
651178479Sjb							keep_looping = 0;
652178479Sjb							break;
653178479Sjb						}
654178479Sjb					}
655178479Sjb				}
656178479Sjb				if (rop == NULL && !NFSBCMP(owp->nfsow_owner,
657178479Sjb				    openown, NFSV4CL_LOCKNAMELEN)) {
658178479Sjb					rop = op;
659178479Sjb					if (lpp == NULL)
660178479Sjb						keep_looping = 0;
661178479Sjb				}
662178479Sjb				if (rop2 == NULL)
663178479Sjb					rop2 = op;
664178479Sjb			}
665178479Sjb			op = LIST_NEXT(op, nfso_list);
666178479Sjb		}
667178479Sjb		owp = LIST_NEXT(owp, nfsow_list);
668178479Sjb	}
669178479Sjb	if (rop == NULL)
670178479Sjb		rop = rop2;
671178479Sjb	if (rop == NULL)
672178479Sjb		return (EBADF);
673178479Sjb	*opp = rop;
674178479Sjb	return (0);
675178479Sjb}
676178479Sjb
677178479Sjb/*
678178479Sjb * Release use of an open owner. Called when open operations are done
679178479Sjb * with the open owner.
680178479Sjb */
681178479SjbAPPLESTATIC void
682178479Sjbnfscl_ownerrelease(struct nfsclowner *owp, __unused int error,
683178479Sjb    __unused int candelete, int unlocked)
684178479Sjb{
685178479Sjb
686178479Sjb	if (owp == NULL)
687178479Sjb		return;
688178479Sjb	NFSLOCKCLSTATE();
689178479Sjb	if (!unlocked)
690178479Sjb		nfscl_lockunlock(&owp->nfsow_rwlock);
691178479Sjb	nfscl_clrelease(owp->nfsow_clp);
692178479Sjb	NFSUNLOCKCLSTATE();
693178479Sjb}
694178479Sjb
695178479Sjb/*
696178479Sjb * Release use of an open structure under an open owner.
697178479Sjb */
698178479SjbAPPLESTATIC void
699178479Sjbnfscl_openrelease(struct nfsclopen *op, int error, int candelete)
700178479Sjb{
701178479Sjb	struct nfsclclient *clp;
702178479Sjb	struct nfsclowner *owp;
703178479Sjb
704178479Sjb	if (op == NULL)
705178479Sjb		return;
706178479Sjb	NFSLOCKCLSTATE();
707178479Sjb	owp = op->nfso_own;
708178479Sjb	nfscl_lockunlock(&owp->nfsow_rwlock);
709178479Sjb	clp = owp->nfsow_clp;
710178479Sjb	if (error && candelete && op->nfso_opencnt == 0)
711178479Sjb		nfscl_freeopen(op, 0);
712178479Sjb	nfscl_clrelease(clp);
713178479Sjb	NFSUNLOCKCLSTATE();
714178479Sjb}
715178479Sjb
716178479Sjb/*
717178479Sjb * Called to get a clientid structure. It will optionally lock the
718178479Sjb * client data structures to do the SetClientId/SetClientId_confirm,
719178479Sjb * but will release that lock and return the clientid with a refernce
720178479Sjb * count on it.
721178479Sjb * If the "cred" argument is NULL, a new clientid should not be created.
722178479Sjb * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot
723178479Sjb * be done.
724178559Sjb * The start_renewthread argument tells nfscl_getcl() to start a renew
725178559Sjb * thread if this creates a new clp.
726178559Sjb * It always clpp with a reference count on it, unless returning an error.
727178559Sjb */
728178559SjbAPPLESTATIC int
729178559Sjbnfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
730178559Sjb    int start_renewthread, struct nfsclclient **clpp)
731178559Sjb{
732178559Sjb	struct nfsclclient *clp;
733178559Sjb	struct nfsclclient *newclp = NULL;
734178559Sjb	struct nfsmount *nmp;
735178559Sjb	char uuid[HOSTUUIDLEN];
736178479Sjb	int igotlock = 0, error, trystalecnt, clidinusedelay, i;
737178479Sjb	u_int16_t idlen = 0;
738178479Sjb
739178479Sjb	nmp = VFSTONFS(mp);
740178479Sjb	if (cred != NULL) {
741178479Sjb		getcredhostuuid(cred, uuid, sizeof uuid);
742178479Sjb		idlen = strlen(uuid);
743178479Sjb		if (idlen > 0)
744178479Sjb			idlen += sizeof (u_int64_t);
745178479Sjb		else
746178479Sjb			idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
747178479Sjb		MALLOC(newclp, struct nfsclclient *,
748178479Sjb		    sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
749178479Sjb		    M_WAITOK | M_ZERO);
750178479Sjb	}
751178479Sjb	NFSLOCKCLSTATE();
752178479Sjb	/*
753178479Sjb	 * If a forced dismount is already in progress, don't
754210425Savg	 * allocate a new clientid and get out now. For the case where
755210425Savg	 * clp != NULL, this is a harmless optimization.
756210425Savg	 */
757210425Savg	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
758210425Savg		NFSUNLOCKCLSTATE();
759210425Savg		if (newclp != NULL)
760178479Sjb			free(newclp, M_NFSCLCLIENT);
761178479Sjb		return (EBADF);
762178479Sjb	}
763178479Sjb	clp = nmp->nm_clp;
764178479Sjb	if (clp == NULL) {
765178479Sjb		if (newclp == NULL) {
766178559Sjb			NFSUNLOCKCLSTATE();
767178479Sjb			return (EACCES);
768178559Sjb		}
769178479Sjb		clp = newclp;
770178559Sjb		clp->nfsc_idlen = idlen;
771178479Sjb		LIST_INIT(&clp->nfsc_owner);
772178479Sjb		TAILQ_INIT(&clp->nfsc_deleg);
773178479Sjb		TAILQ_INIT(&clp->nfsc_layout);
774178479Sjb		LIST_INIT(&clp->nfsc_devinfo);
775178479Sjb		for (i = 0; i < NFSCLDELEGHASHSIZE; i++)
776178479Sjb			LIST_INIT(&clp->nfsc_deleghash[i]);
777178479Sjb		for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++)
778178479Sjb			LIST_INIT(&clp->nfsc_layouthash[i]);
779178479Sjb		clp->nfsc_flags = NFSCLFLAGS_INITED;
780178479Sjb		clp->nfsc_clientidrev = 1;
781178479Sjb		clp->nfsc_cbident = nfscl_nextcbident();
782178479Sjb		nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id,
783178479Sjb		    clp->nfsc_idlen);
784178479Sjb		LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list);
785178479Sjb		nmp->nm_clp = clp;
786178479Sjb		clp->nfsc_nmp = nmp;
787210767Srpaulo		NFSUNLOCKCLSTATE();
788210767Srpaulo		if (start_renewthread != 0)
789210767Srpaulo			nfscl_start_renewthread(clp);
790178479Sjb	} else {
791178479Sjb		NFSUNLOCKCLSTATE();
792178479Sjb		if (newclp != NULL)
793178479Sjb			free(newclp, M_NFSCLCLIENT);
794210767Srpaulo	}
795210767Srpaulo	NFSLOCKCLSTATE();
796210767Srpaulo	while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock &&
797210767Srpaulo	    (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0)
798210767Srpaulo		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
799210767Srpaulo		    NFSCLSTATEMUTEXPTR, mp);
800210767Srpaulo	if (igotlock == 0) {
801210767Srpaulo		/*
802210767Srpaulo		 * Call nfsv4_lock() with "iwantlock == 0" so that it will
803210767Srpaulo		 * wait for a pending exclusive lock request.  This gives the
804210767Srpaulo		 * exclusive lock request priority over this shared lock
805210767Srpaulo		 * request.
806178479Sjb		 * An exclusive lock on nfsc_lock is used mainly for server
807178479Sjb		 * crash recoveries.
808178479Sjb		 */
809178479Sjb		nfsv4_lock(&clp->nfsc_lock, 0, NULL, NFSCLSTATEMUTEXPTR, mp);
810178479Sjb		nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
811178479Sjb	}
812178479Sjb	if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
813178479Sjb		/*
814178479Sjb		 * Both nfsv4_lock() and nfsv4_getref() know to check
815178479Sjb		 * for MNTK_UNMOUNTF and return without sleeping to
816178479Sjb		 * wait for the exclusive lock to be released, since it
817178479Sjb		 * might be held by nfscl_umount() and we need to get out
818178479Sjb		 * now for that case and not wait until nfscl_umount()
819178479Sjb		 * releases it.
820178479Sjb		 */
821178479Sjb		NFSUNLOCKCLSTATE();
822178479Sjb		return (EBADF);
823178479Sjb	}
824178479Sjb	NFSUNLOCKCLSTATE();
825178479Sjb
826178479Sjb	/*
827178479Sjb	 * If it needs a clientid, do the setclientid now.
828178479Sjb	 */
829178479Sjb	if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) {
830178479Sjb		if (!igotlock)
831178479Sjb			panic("nfscl_clget");
832178479Sjb		if (p == NULL || cred == NULL) {
833178479Sjb			NFSLOCKCLSTATE();
834178479Sjb			nfsv4_unlock(&clp->nfsc_lock, 0);
835178479Sjb			NFSUNLOCKCLSTATE();
836178479Sjb			return (EACCES);
837178479Sjb		}
838178479Sjb		/*
839178479Sjb		 * If RFC3530 Sec. 14.2.33 is taken literally,
840178479Sjb		 * NFSERR_CLIDINUSE will be returned persistently for the
841178479Sjb		 * case where a new mount of the same file system is using
842178479Sjb		 * a different principal. In practice, NFSERR_CLIDINUSE is
843178479Sjb		 * only returned when there is outstanding unexpired state
844178479Sjb		 * on the clientid. As such, try for twice the lease
845178479Sjb		 * interval, if we know what that is. Otherwise, make a
846178479Sjb		 * wild ass guess.
847178479Sjb		 * The case of returning NFSERR_STALECLIENTID is far less
848178479Sjb		 * likely, but might occur if there is a significant delay
849178479Sjb		 * between doing the SetClientID and SetClientIDConfirm Ops,
850178479Sjb		 * such that the server throws away the clientid before
851178479Sjb		 * receiving the SetClientIDConfirm.
852178479Sjb		 */
853178479Sjb		if (clp->nfsc_renew > 0)
854178479Sjb			clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
855178479Sjb		else
856178479Sjb			clidinusedelay = 120;
857178479Sjb		trystalecnt = 3;
858178479Sjb		do {
859178479Sjb			error = nfsrpc_setclient(nmp, clp, 0, cred, p);
860178479Sjb			if (error == NFSERR_STALECLIENTID ||
861178479Sjb			    error == NFSERR_STALEDONTRECOVER ||
862178479Sjb			    error == NFSERR_BADSESSION ||
863178479Sjb			    error == NFSERR_CLIDINUSE) {
864178479Sjb				(void) nfs_catnap(PZERO, error, "nfs_setcl");
865178479Sjb			}
866178479Sjb		} while (((error == NFSERR_STALECLIENTID ||
867178479Sjb		     error == NFSERR_BADSESSION ||
868178479Sjb		     error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) ||
869178479Sjb		    (error == NFSERR_CLIDINUSE && --clidinusedelay > 0));
870178479Sjb		if (error) {
871178479Sjb			NFSLOCKCLSTATE();
872178559Sjb			nfsv4_unlock(&clp->nfsc_lock, 0);
873178559Sjb			NFSUNLOCKCLSTATE();
874178559Sjb			return (error);
875178479Sjb		}
876178479Sjb		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
877178559Sjb	}
878178479Sjb	if (igotlock) {
879178559Sjb		NFSLOCKCLSTATE();
880178559Sjb		nfsv4_unlock(&clp->nfsc_lock, 1);
881178559Sjb		NFSUNLOCKCLSTATE();
882178479Sjb	}
883178479Sjb
884178479Sjb	*clpp = clp;
885178479Sjb	return (0);
886178479Sjb}
887178479Sjb
888178479Sjb/*
889178479Sjb * Get a reference to a clientid and return it, if valid.
890178479Sjb */
891178479SjbAPPLESTATIC struct nfsclclient *
892178479Sjbnfscl_findcl(struct nfsmount *nmp)
893178479Sjb{
894178559Sjb	struct nfsclclient *clp;
895178479Sjb
896178479Sjb	clp = nmp->nm_clp;
897178559Sjb	if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID))
898210425Savg		return (NULL);
899178559Sjb	return (clp);
900178559Sjb}
901210425Savg
902178559Sjb/*
903210425Savg * Release the clientid structure. It may be locked or reference counted.
904178479Sjb */
905178559Sjbstatic void
906178559Sjbnfscl_clrelease(struct nfsclclient *clp)
907178559Sjb{
908178559Sjb
909178479Sjb	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
910178479Sjb		nfsv4_unlock(&clp->nfsc_lock, 0);
911178479Sjb	else
912178479Sjb		nfsv4_relref(&clp->nfsc_lock);
913178479Sjb}
914178479Sjb
915178479Sjb/*
916178479Sjb * External call for nfscl_clrelease.
917178479Sjb */
918178479SjbAPPLESTATIC void
919178479Sjbnfscl_clientrelease(struct nfsclclient *clp)
920178479Sjb{
921178479Sjb
922178479Sjb	NFSLOCKCLSTATE();
923178479Sjb	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
924178479Sjb		nfsv4_unlock(&clp->nfsc_lock, 0);
925178479Sjb	else
926178479Sjb		nfsv4_relref(&clp->nfsc_lock);
927210767Srpaulo	NFSUNLOCKCLSTATE();
928178479Sjb}
929178479Sjb
930178479Sjb/*
931178479Sjb * Called when wanting to lock a byte region.
932178479Sjb */
933178479SjbAPPLESTATIC int
934178479Sjbnfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
935178479Sjb    short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp,
936178479Sjb    int recovery, void *id, int flags, u_int8_t *rownp, u_int8_t *ropenownp,
937178479Sjb    struct nfscllockowner **lpp, int *newonep, int *donelocallyp)
938178479Sjb{
939178479Sjb	struct nfscllockowner *lp;
940178479Sjb	struct nfsclopen *op;
941178479Sjb	struct nfsclclient *clp;
942178479Sjb	struct nfscllockowner *nlp;
943178479Sjb	struct nfscllock *nlop, *otherlop;
944178479Sjb	struct nfscldeleg *dp = NULL, *ldp = NULL;
945178479Sjb	struct nfscllockownerhead *lhp = NULL;
946178479Sjb	struct nfsnode *np;
947178479Sjb	u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp, openown[NFSV4CL_LOCKNAMELEN];
948210425Savg	u_int8_t *openownp;
949210425Savg	int error = 0, ret, donelocally = 0;
950210425Savg	u_int32_t mode;
951210425Savg
952210425Savg	/* For Lock Ops, the open mode doesn't matter, so use 0 to match any. */
953210425Savg	mode = 0;
954210425Savg	np = VTONFS(vp);
955210425Savg	*lpp = NULL;
956210425Savg	lp = NULL;
957210425Savg	*newonep = 0;
958210425Savg	*donelocallyp = 0;
959210425Savg
960210425Savg	/*
961210425Savg	 * Might need these, so MALLOC them now, to
962178479Sjb	 * avoid a tsleep() in MALLOC later.
963178479Sjb	 */
964178479Sjb	MALLOC(nlp, struct nfscllockowner *,
965178479Sjb	    sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK);
966178479Sjb	MALLOC(otherlop, struct nfscllock *,
967178479Sjb	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
968178479Sjb	MALLOC(nlop, struct nfscllock *,
969178479Sjb	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
970210425Savg	nlop->nfslo_type = type;
971210425Savg	nlop->nfslo_first = off;
972210425Savg	if (len == NFS64BITSSET) {
973210425Savg		nlop->nfslo_end = NFS64BITSSET;
974210425Savg	} else {
975210425Savg		nlop->nfslo_end = off + len;
976210425Savg		if (nlop->nfslo_end <= nlop->nfslo_first)
977210425Savg			error = NFSERR_INVAL;
978210425Savg	}
979210425Savg
980210425Savg	if (!error) {
981210425Savg		if (recovery)
982210425Savg			clp = rclp;
983178479Sjb		else
984178479Sjb			error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
985178479Sjb	}
986178479Sjb	if (error) {
987178479Sjb		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
988178479Sjb		FREE((caddr_t)otherlop, M_NFSCLLOCK);
989178479Sjb		FREE((caddr_t)nlop, M_NFSCLLOCK);
990178479Sjb		return (error);
991178479Sjb	}
992178479Sjb
993178479Sjb	op = NULL;
994178479Sjb	if (recovery) {
995178479Sjb		ownp = rownp;
996178479Sjb		openownp = ropenownp;
997178479Sjb	} else {
998178479Sjb		nfscl_filllockowner(id, own, flags);
999178479Sjb		ownp = own;
1000178479Sjb		nfscl_filllockowner(p->td_proc, openown, F_POSIX);
1001178479Sjb		openownp = openown;
1002178479Sjb	}
1003178479Sjb	if (!recovery) {
1004178559Sjb		NFSLOCKCLSTATE();
1005178479Sjb		/*
1006178559Sjb		 * First, search for a delegation. If one exists for this file,
1007210425Savg		 * the lock can be done locally against it, so long as there
1008210425Savg		 * isn't a local lock conflict.
1009210425Savg		 */
1010210425Savg		ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1011210425Savg		    np->n_fhp->nfh_len);
1012210425Savg		/* Just sanity check for correct type of delegation */
1013210425Savg		if (dp != NULL && ((dp->nfsdl_flags &
1014178559Sjb		    (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 ||
1015178559Sjb		     (type == F_WRLCK &&
1016178559Sjb		      (dp->nfsdl_flags & NFSCLDL_WRITE) == 0)))
1017178559Sjb			dp = NULL;
1018178559Sjb	}
1019178559Sjb	if (dp != NULL) {
1020178559Sjb		/* Now, find an open and maybe a lockowner. */
1021178559Sjb		ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh,
1022178559Sjb		    np->n_fhp->nfh_len, openownp, ownp, mode, NULL, &op);
1023178559Sjb		if (ret)
1024178559Sjb			ret = nfscl_getopen(&clp->nfsc_owner,
1025178559Sjb			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1026178559Sjb			    ownp, mode, NULL, &op);
1027178559Sjb		if (!ret) {
1028178479Sjb			lhp = &dp->nfsdl_lock;
1029178479Sjb			TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
1030178479Sjb			TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
1031178479Sjb			dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
1032178479Sjb			donelocally = 1;
1033178479Sjb		} else {
1034178479Sjb			dp = NULL;
1035178479Sjb		}
1036178479Sjb	}
1037178479Sjb	if (!donelocally) {
1038178479Sjb		/*
1039178479Sjb		 * Get the related Open and maybe lockowner.
1040178479Sjb		 */
1041178479Sjb		error = nfscl_getopen(&clp->nfsc_owner,
1042178479Sjb		    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1043178479Sjb		    ownp, mode, &lp, &op);
1044178479Sjb		if (!error)
1045178559Sjb			lhp = &op->nfso_lock;
1046178559Sjb	}
1047178559Sjb	if (!error && !recovery)
1048178479Sjb		error = nfscl_localconflict(clp, np->n_fhp->nfh_fh,
1049178479Sjb		    np->n_fhp->nfh_len, nlop, ownp, ldp, NULL);
1050178479Sjb	if (error) {
1051178479Sjb		if (!recovery) {
1052178479Sjb			nfscl_clrelease(clp);
1053178559Sjb			NFSUNLOCKCLSTATE();
1054178479Sjb		}
1055178479Sjb		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1056178479Sjb		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1057178479Sjb		FREE((caddr_t)nlop, M_NFSCLLOCK);
1058178479Sjb		return (error);
1059178479Sjb	}
1060178479Sjb
1061178479Sjb	/*
1062178479Sjb	 * Ok, see if a lockowner exists and create one, as required.
1063178479Sjb	 */
1064178479Sjb	if (lp == NULL)
1065178479Sjb		LIST_FOREACH(lp, lhp, nfsl_list) {
1066178479Sjb			if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
1067178479Sjb				break;
1068178479Sjb		}
1069178559Sjb	if (lp == NULL) {
1070178559Sjb		NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
1071178559Sjb		if (recovery)
1072178559Sjb			NFSBCOPY(ropenownp, nlp->nfsl_openowner,
1073178559Sjb			    NFSV4CL_LOCKNAMELEN);
1074178559Sjb		else
1075178559Sjb			NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner,
1076178559Sjb			    NFSV4CL_LOCKNAMELEN);
1077178559Sjb		nlp->nfsl_seqid = 0;
1078178559Sjb		nlp->nfsl_lockflags = flags;
1079178559Sjb		nlp->nfsl_inprog = NULL;
1080178559Sjb		nfscl_lockinit(&nlp->nfsl_rwlock);
1081178479Sjb		LIST_INIT(&nlp->nfsl_lock);
1082178479Sjb		if (donelocally) {
1083178479Sjb			nlp->nfsl_open = NULL;
1084178479Sjb			newnfsstats.cllocallockowners++;
1085178479Sjb		} else {
1086178479Sjb			nlp->nfsl_open = op;
1087178479Sjb			newnfsstats.cllockowners++;
1088178479Sjb		}
1089178479Sjb		LIST_INSERT_HEAD(lhp, nlp, nfsl_list);
1090178479Sjb		lp = nlp;
1091178479Sjb		nlp = NULL;
1092178479Sjb		*newonep = 1;
1093178559Sjb	}
1094178479Sjb
1095178559Sjb	/*
1096178479Sjb	 * Now, update the byte ranges for locks.
1097178559Sjb	 */
1098178479Sjb	ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally);
1099178559Sjb	if (!ret)
1100178479Sjb		donelocally = 1;
1101178479Sjb	if (donelocally) {
1102178479Sjb		*donelocallyp = 1;
1103178479Sjb		if (!recovery)
1104178479Sjb			nfscl_clrelease(clp);
1105178479Sjb	} else {
1106178479Sjb		/*
1107178479Sjb		 * Serial modifications on the lock owner for multiple threads
1108178479Sjb		 * for the same process using a read/write lock.
1109178479Sjb		 */
1110178479Sjb		if (!recovery)
1111178479Sjb			nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1112178479Sjb	}
1113178479Sjb	if (!recovery)
1114178479Sjb		NFSUNLOCKCLSTATE();
1115178479Sjb
1116178479Sjb	if (nlp)
1117178479Sjb		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1118178479Sjb	if (nlop)
1119178479Sjb		FREE((caddr_t)nlop, M_NFSCLLOCK);
1120178479Sjb	if (otherlop)
1121178479Sjb		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1122178479Sjb
1123178479Sjb	*lpp = lp;
1124178479Sjb	return (0);
1125178479Sjb}
1126178479Sjb
1127178479Sjb/*
1128178479Sjb * Called to unlock a byte range, for LockU.
1129178479Sjb */
1130178479SjbAPPLESTATIC int
1131178479Sjbnfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
1132178479Sjb    __unused struct ucred *cred, NFSPROC_T *p, int callcnt,
1133178479Sjb    struct nfsclclient *clp, void *id, int flags,
1134178479Sjb    struct nfscllockowner **lpp, int *dorpcp)
1135178479Sjb{
1136178479Sjb	struct nfscllockowner *lp;
1137178479Sjb	struct nfsclowner *owp;
1138178479Sjb	struct nfsclopen *op;
1139178479Sjb	struct nfscllock *nlop, *other_lop = NULL;
1140178479Sjb	struct nfscldeleg *dp;
1141178479Sjb	struct nfsnode *np;
1142178479Sjb	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1143178479Sjb	int ret = 0, fnd;
1144178479Sjb
1145178479Sjb	np = VTONFS(vp);
1146178479Sjb	*lpp = NULL;
1147178479Sjb	*dorpcp = 0;
1148178479Sjb
1149178479Sjb	/*
1150178479Sjb	 * Might need these, so MALLOC them now, to
1151178479Sjb	 * avoid a tsleep() in MALLOC later.
1152178479Sjb	 */
1153178479Sjb	MALLOC(nlop, struct nfscllock *,
1154178479Sjb	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1155178479Sjb	nlop->nfslo_type = F_UNLCK;
1156178479Sjb	nlop->nfslo_first = off;
1157178479Sjb	if (len == NFS64BITSSET) {
1158178479Sjb		nlop->nfslo_end = NFS64BITSSET;
1159178479Sjb	} else {
1160178479Sjb		nlop->nfslo_end = off + len;
1161178479Sjb		if (nlop->nfslo_end <= nlop->nfslo_first) {
1162178479Sjb			FREE((caddr_t)nlop, M_NFSCLLOCK);
1163178479Sjb			return (NFSERR_INVAL);
1164178479Sjb		}
1165178479Sjb	}
1166178479Sjb	if (callcnt == 0) {
1167178479Sjb		MALLOC(other_lop, struct nfscllock *,
1168178479Sjb		    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1169178479Sjb		*other_lop = *nlop;
1170178479Sjb	}
1171178479Sjb	nfscl_filllockowner(id, own, flags);
1172178479Sjb	dp = NULL;
1173178479Sjb	NFSLOCKCLSTATE();
1174178479Sjb	if (callcnt == 0)
1175178479Sjb		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1176178479Sjb		    np->n_fhp->nfh_len);
1177178479Sjb
1178178479Sjb	/*
1179178479Sjb	 * First, unlock any local regions on a delegation.
1180178479Sjb	 */
1181178479Sjb	if (dp != NULL) {
1182178479Sjb		/* Look for this lockowner. */
1183178479Sjb		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1184178479Sjb			if (!NFSBCMP(lp->nfsl_owner, own,
1185178479Sjb			    NFSV4CL_LOCKNAMELEN))
1186178479Sjb				break;
1187178479Sjb		}
1188178479Sjb		if (lp != NULL)
1189178479Sjb			/* Use other_lop, so nlop is still available */
1190178479Sjb			(void)nfscl_updatelock(lp, &other_lop, NULL, 1);
1191178479Sjb	}
1192178479Sjb
1193178479Sjb	/*
1194178479Sjb	 * Now, find a matching open/lockowner that hasn't already been done,
1195178479Sjb	 * as marked by nfsl_inprog.
1196178479Sjb	 */
1197178479Sjb	lp = NULL;
1198178479Sjb	fnd = 0;
1199178479Sjb	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1200178479Sjb	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1201178479Sjb		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1202178479Sjb		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1203178479Sjb		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1204178479Sjb			if (lp->nfsl_inprog == NULL &&
1205178479Sjb			    !NFSBCMP(lp->nfsl_owner, own,
1206178479Sjb			     NFSV4CL_LOCKNAMELEN)) {
1207178479Sjb				fnd = 1;
1208178479Sjb				break;
1209178479Sjb			}
1210178479Sjb		    }
1211178479Sjb		    if (fnd)
1212178479Sjb			break;
1213178479Sjb		}
1214178479Sjb	    }
1215178479Sjb	    if (fnd)
1216178479Sjb		break;
1217178479Sjb	}
1218178479Sjb
1219178479Sjb	if (lp != NULL) {
1220178479Sjb		ret = nfscl_updatelock(lp, &nlop, NULL, 0);
1221178479Sjb		if (ret)
1222178479Sjb			*dorpcp = 1;
1223178479Sjb		/*
1224178479Sjb		 * Serial modifications on the lock owner for multiple
1225178479Sjb		 * threads for the same process using a read/write lock.
1226178479Sjb		 */
1227178479Sjb		lp->nfsl_inprog = p;
1228178479Sjb		nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1229178479Sjb		*lpp = lp;
1230178479Sjb	}
1231178479Sjb	NFSUNLOCKCLSTATE();
1232178479Sjb	if (nlop)
1233178479Sjb		FREE((caddr_t)nlop, M_NFSCLLOCK);
1234178479Sjb	if (other_lop)
1235178479Sjb		FREE((caddr_t)other_lop, M_NFSCLLOCK);
1236178479Sjb	return (0);
1237178479Sjb}
1238178479Sjb
1239178479Sjb/*
1240178479Sjb * Release all lockowners marked in progess for this process and file.
1241178479Sjb */
1242178479SjbAPPLESTATIC void
1243178479Sjbnfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p,
1244178479Sjb    void *id, int flags)
1245178479Sjb{
1246178479Sjb	struct nfsclowner *owp;
1247178479Sjb	struct nfsclopen *op;
1248178479Sjb	struct nfscllockowner *lp;
1249178479Sjb	struct nfsnode *np;
1250178479Sjb	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1251178479Sjb
1252178479Sjb	np = VTONFS(vp);
1253178479Sjb	nfscl_filllockowner(id, own, flags);
1254178479Sjb	NFSLOCKCLSTATE();
1255178479Sjb	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1256178479Sjb	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1257178479Sjb		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1258178479Sjb		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1259178479Sjb		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1260178479Sjb			if (lp->nfsl_inprog == p &&
1261178479Sjb			    !NFSBCMP(lp->nfsl_owner, own,
1262178479Sjb			    NFSV4CL_LOCKNAMELEN)) {
1263178479Sjb			    lp->nfsl_inprog = NULL;
1264178479Sjb			    nfscl_lockunlock(&lp->nfsl_rwlock);
1265178479Sjb			}
1266178479Sjb		    }
1267178479Sjb		}
1268178479Sjb	    }
1269178479Sjb	}
1270178479Sjb	nfscl_clrelease(clp);
1271178479Sjb	NFSUNLOCKCLSTATE();
1272178479Sjb}
1273178479Sjb
1274178479Sjb/*
1275178479Sjb * Called to find out if any bytes within the byte range specified are
1276178479Sjb * write locked by the calling process. Used to determine if flushing
1277178479Sjb * is required before a LockU.
1278178479Sjb * If in doubt, return 1, so the flush will occur.
1279178479Sjb */
1280178479SjbAPPLESTATIC int
1281178479Sjbnfscl_checkwritelocked(vnode_t vp, struct flock *fl,
1282178479Sjb    struct ucred *cred, NFSPROC_T *p, void *id, int flags)
1283178479Sjb{
1284178479Sjb	struct nfsclowner *owp;
1285178479Sjb	struct nfscllockowner *lp;
1286178479Sjb	struct nfsclopen *op;
1287178479Sjb	struct nfsclclient *clp;
1288178479Sjb	struct nfscllock *lop;
1289178479Sjb	struct nfscldeleg *dp;
1290178479Sjb	struct nfsnode *np;
1291178479Sjb	u_int64_t off, end;
1292178479Sjb	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1293178479Sjb	int error = 0;
1294178479Sjb
1295178479Sjb	np = VTONFS(vp);
1296178479Sjb	switch (fl->l_whence) {
1297178479Sjb	case SEEK_SET:
1298178479Sjb	case SEEK_CUR:
1299178479Sjb		/*
1300178479Sjb		 * Caller is responsible for adding any necessary offset
1301178479Sjb		 * when SEEK_CUR is used.
1302178479Sjb		 */
1303178479Sjb		off = fl->l_start;
1304178479Sjb		break;
1305178479Sjb	case SEEK_END:
1306178479Sjb		off = np->n_size + fl->l_start;
1307178479Sjb		break;
1308178479Sjb	default:
1309178479Sjb		return (1);
1310178479Sjb	};
1311178479Sjb	if (fl->l_len != 0) {
1312178479Sjb		end = off + fl->l_len;
1313178479Sjb		if (end < off)
1314178479Sjb			return (1);
1315178479Sjb	} else {
1316178479Sjb		end = NFS64BITSSET;
1317178479Sjb	}
1318178479Sjb
1319178479Sjb	error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
1320178479Sjb	if (error)
1321178479Sjb		return (1);
1322178479Sjb	nfscl_filllockowner(id, own, flags);
1323178479Sjb	NFSLOCKCLSTATE();
1324178479Sjb
1325178479Sjb	/*
1326178479Sjb	 * First check the delegation locks.
1327178479Sjb	 */
1328178479Sjb	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
1329178479Sjb	if (dp != NULL) {
1330178479Sjb		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1331178479Sjb			if (!NFSBCMP(lp->nfsl_owner, own,
1332178479Sjb			    NFSV4CL_LOCKNAMELEN))
1333178479Sjb				break;
1334178479Sjb		}
1335178479Sjb		if (lp != NULL) {
1336178479Sjb			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1337178479Sjb				if (lop->nfslo_first >= end)
1338178479Sjb					break;
1339178479Sjb				if (lop->nfslo_end <= off)
1340178479Sjb					continue;
1341178479Sjb				if (lop->nfslo_type == F_WRLCK) {
1342178479Sjb					nfscl_clrelease(clp);
1343178479Sjb					NFSUNLOCKCLSTATE();
1344178479Sjb					return (1);
1345178479Sjb				}
1346178479Sjb			}
1347178479Sjb		}
1348178479Sjb	}
1349178479Sjb
1350178479Sjb	/*
1351178479Sjb	 * Now, check state against the server.
1352178479Sjb	 */
1353178479Sjb	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1354178479Sjb	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1355178479Sjb		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1356178479Sjb		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1357178479Sjb		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1358178479Sjb			if (!NFSBCMP(lp->nfsl_owner, own,
1359178479Sjb			    NFSV4CL_LOCKNAMELEN))
1360178479Sjb			    break;
1361178479Sjb		    }
1362178479Sjb		    if (lp != NULL) {
1363178479Sjb			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1364178479Sjb			    if (lop->nfslo_first >= end)
1365178479Sjb				break;
1366178479Sjb			    if (lop->nfslo_end <= off)
1367178479Sjb				continue;
1368178479Sjb			    if (lop->nfslo_type == F_WRLCK) {
1369178479Sjb				nfscl_clrelease(clp);
1370178479Sjb				NFSUNLOCKCLSTATE();
1371178479Sjb				return (1);
1372178479Sjb			    }
1373178479Sjb			}
1374178479Sjb		    }
1375178479Sjb		}
1376178479Sjb	    }
1377178479Sjb	}
1378178479Sjb	nfscl_clrelease(clp);
1379178479Sjb	NFSUNLOCKCLSTATE();
1380178479Sjb	return (0);
1381178479Sjb}
1382178479Sjb
1383178479Sjb/*
1384178479Sjb * Release a byte range lock owner structure.
1385178479Sjb */
1386178479SjbAPPLESTATIC void
1387178479Sjbnfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete)
1388178479Sjb{
1389178479Sjb	struct nfsclclient *clp;
1390178479Sjb
1391178479Sjb	if (lp == NULL)
1392178479Sjb		return;
1393178479Sjb	NFSLOCKCLSTATE();
1394178479Sjb	clp = lp->nfsl_open->nfso_own->nfsow_clp;
1395178479Sjb	if (error != 0 && candelete &&
1396178479Sjb	    (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0)
1397178479Sjb		nfscl_freelockowner(lp, 0);
1398178479Sjb	else
1399178479Sjb		nfscl_lockunlock(&lp->nfsl_rwlock);
1400178479Sjb	nfscl_clrelease(clp);
1401178479Sjb	NFSUNLOCKCLSTATE();
1402178479Sjb}
1403178479Sjb
1404178479Sjb/*
1405178479Sjb * Free up an open structure and any associated byte range lock structures.
1406178479Sjb */
1407178479SjbAPPLESTATIC void
1408178479Sjbnfscl_freeopen(struct nfsclopen *op, int local)
1409178479Sjb{
1410178479Sjb
1411178479Sjb	LIST_REMOVE(op, nfso_list);
1412178479Sjb	nfscl_freealllocks(&op->nfso_lock, local);
1413178479Sjb	FREE((caddr_t)op, M_NFSCLOPEN);
1414178479Sjb	if (local)
1415178479Sjb		newnfsstats.cllocalopens--;
1416178479Sjb	else
1417178479Sjb		newnfsstats.clopens--;
1418178479Sjb}
1419178479Sjb
1420178479Sjb/*
1421178479Sjb * Free up all lock owners and associated locks.
1422178479Sjb */
1423178479Sjbstatic void
1424178479Sjbnfscl_freealllocks(struct nfscllockownerhead *lhp, int local)
1425178479Sjb{
1426178479Sjb	struct nfscllockowner *lp, *nlp;
1427178479Sjb
1428178479Sjb	LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) {
1429178479Sjb		if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1430178479Sjb			panic("nfscllckw");
1431178479Sjb		nfscl_freelockowner(lp, local);
1432178479Sjb	}
1433178479Sjb}
1434178479Sjb
1435178479Sjb/*
1436178479Sjb * Called for an Open when NFSERR_EXPIRED is received from the server.
1437178479Sjb * If there are no byte range locks nor a Share Deny lost, try to do a
1438178479Sjb * fresh Open. Otherwise, free the open.
1439178479Sjb */
1440178479Sjbstatic int
1441178479Sjbnfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op,
1442178479Sjb    struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
1443178479Sjb{
1444178479Sjb	struct nfscllockowner *lp;
1445178479Sjb	struct nfscldeleg *dp;
1446178479Sjb	int mustdelete = 0, error;
1447178479Sjb
1448178479Sjb	/*
1449178479Sjb	 * Look for any byte range lock(s).
1450178479Sjb	 */
1451178479Sjb	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1452178479Sjb		if (!LIST_EMPTY(&lp->nfsl_lock)) {
1453178479Sjb			mustdelete = 1;
1454178479Sjb			break;
1455178479Sjb		}
1456178479Sjb	}
1457178479Sjb
1458178479Sjb	/*
1459178479Sjb	 * If no byte range lock(s) nor a Share deny, try to re-open.
1460178479Sjb	 */
1461178479Sjb	if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) {
1462178479Sjb		newnfs_copycred(&op->nfso_cred, cred);
1463178479Sjb		dp = NULL;
1464178479Sjb		error = nfsrpc_reopen(nmp, op->nfso_fh,
1465178479Sjb		    op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p);
1466178479Sjb		if (error) {
1467178479Sjb			mustdelete = 1;
1468			if (dp != NULL) {
1469				FREE((caddr_t)dp, M_NFSCLDELEG);
1470				dp = NULL;
1471			}
1472		}
1473		if (dp != NULL)
1474			nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh,
1475			    op->nfso_fhlen, cred, p, &dp);
1476	}
1477
1478	/*
1479	 * If a byte range lock or Share deny or couldn't re-open, free it.
1480	 */
1481	if (mustdelete)
1482		nfscl_freeopen(op, 0);
1483	return (mustdelete);
1484}
1485
1486/*
1487 * Free up an open owner structure.
1488 */
1489static void
1490nfscl_freeopenowner(struct nfsclowner *owp, int local)
1491{
1492
1493	LIST_REMOVE(owp, nfsow_list);
1494	FREE((caddr_t)owp, M_NFSCLOWNER);
1495	if (local)
1496		newnfsstats.cllocalopenowners--;
1497	else
1498		newnfsstats.clopenowners--;
1499}
1500
1501/*
1502 * Free up a byte range lock owner structure.
1503 */
1504APPLESTATIC void
1505nfscl_freelockowner(struct nfscllockowner *lp, int local)
1506{
1507	struct nfscllock *lop, *nlop;
1508
1509	LIST_REMOVE(lp, nfsl_list);
1510	LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
1511		nfscl_freelock(lop, local);
1512	}
1513	FREE((caddr_t)lp, M_NFSCLLOCKOWNER);
1514	if (local)
1515		newnfsstats.cllocallockowners--;
1516	else
1517		newnfsstats.cllockowners--;
1518}
1519
1520/*
1521 * Free up a byte range lock structure.
1522 */
1523APPLESTATIC void
1524nfscl_freelock(struct nfscllock *lop, int local)
1525{
1526
1527	LIST_REMOVE(lop, nfslo_list);
1528	FREE((caddr_t)lop, M_NFSCLLOCK);
1529	if (local)
1530		newnfsstats.cllocallocks--;
1531	else
1532		newnfsstats.cllocks--;
1533}
1534
1535/*
1536 * Clean out the state related to a delegation.
1537 */
1538static void
1539nfscl_cleandeleg(struct nfscldeleg *dp)
1540{
1541	struct nfsclowner *owp, *nowp;
1542	struct nfsclopen *op;
1543
1544	LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
1545		op = LIST_FIRST(&owp->nfsow_open);
1546		if (op != NULL) {
1547			if (LIST_NEXT(op, nfso_list) != NULL)
1548				panic("nfscleandel");
1549			nfscl_freeopen(op, 1);
1550		}
1551		nfscl_freeopenowner(owp, 1);
1552	}
1553	nfscl_freealllocks(&dp->nfsdl_lock, 1);
1554}
1555
1556/*
1557 * Free a delegation.
1558 */
1559static void
1560nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp)
1561{
1562
1563	TAILQ_REMOVE(hdp, dp, nfsdl_list);
1564	LIST_REMOVE(dp, nfsdl_hash);
1565	FREE((caddr_t)dp, M_NFSCLDELEG);
1566	newnfsstats.cldelegates--;
1567	nfscl_delegcnt--;
1568}
1569
1570/*
1571 * Free up all state related to this client structure.
1572 */
1573static void
1574nfscl_cleanclient(struct nfsclclient *clp)
1575{
1576	struct nfsclowner *owp, *nowp;
1577	struct nfsclopen *op, *nop;
1578
1579	/* Now, all the OpenOwners, etc. */
1580	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1581		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1582			nfscl_freeopen(op, 0);
1583		}
1584		nfscl_freeopenowner(owp, 0);
1585	}
1586}
1587
1588/*
1589 * Called when an NFSERR_EXPIRED is received from the server.
1590 */
1591static void
1592nfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
1593    struct ucred *cred, NFSPROC_T *p)
1594{
1595	struct nfsclowner *owp, *nowp, *towp;
1596	struct nfsclopen *op, *nop, *top;
1597	struct nfscldeleg *dp, *ndp;
1598	int ret, printed = 0;
1599
1600	/*
1601	 * First, merge locally issued Opens into the list for the server.
1602	 */
1603	dp = TAILQ_FIRST(&clp->nfsc_deleg);
1604	while (dp != NULL) {
1605	    ndp = TAILQ_NEXT(dp, nfsdl_list);
1606	    owp = LIST_FIRST(&dp->nfsdl_owner);
1607	    while (owp != NULL) {
1608		nowp = LIST_NEXT(owp, nfsow_list);
1609		op = LIST_FIRST(&owp->nfsow_open);
1610		if (op != NULL) {
1611		    if (LIST_NEXT(op, nfso_list) != NULL)
1612			panic("nfsclexp");
1613		    LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) {
1614			if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner,
1615			    NFSV4CL_LOCKNAMELEN))
1616			    break;
1617		    }
1618		    if (towp != NULL) {
1619			/* Merge opens in */
1620			LIST_FOREACH(top, &towp->nfsow_open, nfso_list) {
1621			    if (top->nfso_fhlen == op->nfso_fhlen &&
1622				!NFSBCMP(top->nfso_fh, op->nfso_fh,
1623				 op->nfso_fhlen)) {
1624				top->nfso_mode |= op->nfso_mode;
1625				top->nfso_opencnt += op->nfso_opencnt;
1626				break;
1627			    }
1628			}
1629			if (top == NULL) {
1630			    /* Just add the open to the owner list */
1631			    LIST_REMOVE(op, nfso_list);
1632			    op->nfso_own = towp;
1633			    LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list);
1634			    newnfsstats.cllocalopens--;
1635			    newnfsstats.clopens++;
1636			}
1637		    } else {
1638			/* Just add the openowner to the client list */
1639			LIST_REMOVE(owp, nfsow_list);
1640			owp->nfsow_clp = clp;
1641			LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list);
1642			newnfsstats.cllocalopenowners--;
1643			newnfsstats.clopenowners++;
1644			newnfsstats.cllocalopens--;
1645			newnfsstats.clopens++;
1646		    }
1647		}
1648		owp = nowp;
1649	    }
1650	    if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) {
1651		printed = 1;
1652		printf("nfsv4 expired locks lost\n");
1653	    }
1654	    nfscl_cleandeleg(dp);
1655	    nfscl_freedeleg(&clp->nfsc_deleg, dp);
1656	    dp = ndp;
1657	}
1658	if (!TAILQ_EMPTY(&clp->nfsc_deleg))
1659	    panic("nfsclexp");
1660
1661	/*
1662	 * Now, try and reopen against the server.
1663	 */
1664	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1665		owp->nfsow_seqid = 0;
1666		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1667			ret = nfscl_expireopen(clp, op, nmp, cred, p);
1668			if (ret && !printed) {
1669				printed = 1;
1670				printf("nfsv4 expired locks lost\n");
1671			}
1672		}
1673		if (LIST_EMPTY(&owp->nfsow_open))
1674			nfscl_freeopenowner(owp, 0);
1675	}
1676}
1677
1678/*
1679 * This function must be called after the process represented by "own" has
1680 * exited. Must be called with CLSTATE lock held.
1681 */
1682static void
1683nfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own)
1684{
1685	struct nfsclowner *owp, *nowp;
1686	struct nfscllockowner *lp, *nlp;
1687	struct nfscldeleg *dp;
1688
1689	/* First, get rid of local locks on delegations. */
1690	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1691		LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) {
1692		    if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
1693			if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1694			    panic("nfscllckw");
1695			nfscl_freelockowner(lp, 1);
1696		    }
1697		}
1698	}
1699	owp = LIST_FIRST(&clp->nfsc_owner);
1700	while (owp != NULL) {
1701		nowp = LIST_NEXT(owp, nfsow_list);
1702		if (!NFSBCMP(owp->nfsow_owner, own,
1703		    NFSV4CL_LOCKNAMELEN)) {
1704			/*
1705			 * If there are children that haven't closed the
1706			 * file descriptors yet, the opens will still be
1707			 * here. For that case, let the renew thread clear
1708			 * out the OpenOwner later.
1709			 */
1710			if (LIST_EMPTY(&owp->nfsow_open))
1711				nfscl_freeopenowner(owp, 0);
1712			else
1713				owp->nfsow_defunct = 1;
1714		}
1715		owp = nowp;
1716	}
1717}
1718
1719/*
1720 * Find open/lock owners for processes that have exited.
1721 */
1722static void
1723nfscl_cleanupkext(struct nfsclclient *clp, struct nfscllockownerfhhead *lhp)
1724{
1725	struct nfsclowner *owp, *nowp;
1726	struct nfsclopen *op;
1727	struct nfscllockowner *lp, *nlp;
1728
1729	NFSPROCLISTLOCK();
1730	NFSLOCKCLSTATE();
1731	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1732		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1733			LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp) {
1734				if (LIST_EMPTY(&lp->nfsl_lock))
1735					nfscl_emptylockowner(lp, lhp);
1736			}
1737		}
1738		if (nfscl_procdoesntexist(owp->nfsow_owner))
1739			nfscl_cleanup_common(clp, owp->nfsow_owner);
1740	}
1741	NFSUNLOCKCLSTATE();
1742	NFSPROCLISTUNLOCK();
1743}
1744
1745/*
1746 * Take the empty lock owner and move it to the local lhp list if the
1747 * associated process no longer exists.
1748 */
1749static void
1750nfscl_emptylockowner(struct nfscllockowner *lp,
1751    struct nfscllockownerfhhead *lhp)
1752{
1753	struct nfscllockownerfh *lfhp, *mylfhp;
1754	struct nfscllockowner *nlp;
1755	int fnd_it;
1756
1757	/* If not a Posix lock owner, just return. */
1758	if ((lp->nfsl_lockflags & F_POSIX) == 0)
1759		return;
1760
1761	fnd_it = 0;
1762	mylfhp = NULL;
1763	/*
1764	 * First, search to see if this lock owner is already in the list.
1765	 * If it is, then the associated process no longer exists.
1766	 */
1767	SLIST_FOREACH(lfhp, lhp, nfslfh_list) {
1768		if (lfhp->nfslfh_len == lp->nfsl_open->nfso_fhlen &&
1769		    !NFSBCMP(lfhp->nfslfh_fh, lp->nfsl_open->nfso_fh,
1770		    lfhp->nfslfh_len))
1771			mylfhp = lfhp;
1772		LIST_FOREACH(nlp, &lfhp->nfslfh_lock, nfsl_list)
1773			if (!NFSBCMP(nlp->nfsl_owner, lp->nfsl_owner,
1774			    NFSV4CL_LOCKNAMELEN))
1775				fnd_it = 1;
1776	}
1777	/* If not found, check if process still exists. */
1778	if (fnd_it == 0 && nfscl_procdoesntexist(lp->nfsl_owner) == 0)
1779		return;
1780
1781	/* Move the lock owner over to the local list. */
1782	if (mylfhp == NULL) {
1783		mylfhp = malloc(sizeof(struct nfscllockownerfh), M_TEMP,
1784		    M_NOWAIT);
1785		if (mylfhp == NULL)
1786			return;
1787		mylfhp->nfslfh_len = lp->nfsl_open->nfso_fhlen;
1788		NFSBCOPY(lp->nfsl_open->nfso_fh, mylfhp->nfslfh_fh,
1789		    mylfhp->nfslfh_len);
1790		LIST_INIT(&mylfhp->nfslfh_lock);
1791		SLIST_INSERT_HEAD(lhp, mylfhp, nfslfh_list);
1792	}
1793	LIST_REMOVE(lp, nfsl_list);
1794	LIST_INSERT_HEAD(&mylfhp->nfslfh_lock, lp, nfsl_list);
1795}
1796
1797static int	fake_global;	/* Used to force visibility of MNTK_UNMOUNTF */
1798/*
1799 * Called from nfs umount to free up the clientid.
1800 */
1801APPLESTATIC void
1802nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
1803{
1804	struct nfsclclient *clp;
1805	struct ucred *cred;
1806	int igotlock;
1807
1808	/*
1809	 * For the case that matters, this is the thread that set
1810	 * MNTK_UNMOUNTF, so it will see it set. The code that follows is
1811	 * done to ensure that any thread executing nfscl_getcl() after
1812	 * this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the
1813	 * mutex for NFSLOCKCLSTATE(), so it is "m" for the following
1814	 * explanation, courtesy of Alan Cox.
1815	 * What follows is a snippet from Alan Cox's email at:
1816	 * http://docs.FreeBSD.org/cgi/
1817	 *     mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw
1818	 *
1819	 * 1. Set MNTK_UNMOUNTF
1820	 * 2. Acquire a standard FreeBSD mutex "m".
1821	 * 3. Update some data structures.
1822	 * 4. Release mutex "m".
1823	 *
1824	 * Then, other threads that acquire "m" after step 4 has occurred will
1825	 * see MNTK_UNMOUNTF as set.  But, other threads that beat thread X to
1826	 * step 2 may or may not see MNTK_UNMOUNTF as set.
1827	 */
1828	NFSLOCKCLSTATE();
1829	if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1830		fake_global++;
1831		NFSUNLOCKCLSTATE();
1832		NFSLOCKCLSTATE();
1833	}
1834
1835	clp = nmp->nm_clp;
1836	if (clp != NULL) {
1837		if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
1838			panic("nfscl umount");
1839
1840		/*
1841		 * First, handshake with the nfscl renew thread, to terminate
1842		 * it.
1843		 */
1844		clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
1845		while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
1846			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT,
1847			    "nfsclumnt", hz);
1848
1849		/*
1850		 * Now, get the exclusive lock on the client state, so
1851		 * that no uses of the state are still in progress.
1852		 */
1853		do {
1854			igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1855			    NFSCLSTATEMUTEXPTR, NULL);
1856		} while (!igotlock);
1857		NFSUNLOCKCLSTATE();
1858
1859		/*
1860		 * Free up all the state. It will expire on the server, but
1861		 * maybe we should do a SetClientId/SetClientIdConfirm so
1862		 * the server throws it away?
1863		 */
1864		LIST_REMOVE(clp, nfsc_list);
1865		nfscl_delegreturnall(clp, p);
1866		cred = newnfs_getcred();
1867		if (NFSHASNFSV4N(nmp)) {
1868			(void)nfsrpc_destroysession(nmp, clp, cred, p);
1869			(void)nfsrpc_destroyclient(nmp, clp, cred, p);
1870		} else
1871			(void)nfsrpc_setclient(nmp, clp, 0, cred, p);
1872		nfscl_cleanclient(clp);
1873		nmp->nm_clp = NULL;
1874		NFSFREECRED(cred);
1875		free(clp, M_NFSCLCLIENT);
1876	} else
1877		NFSUNLOCKCLSTATE();
1878}
1879
1880/*
1881 * This function is called when a server replies with NFSERR_STALECLIENTID
1882 * NFSERR_STALESTATEID or NFSERR_BADSESSION. It traverses the clientid lists,
1883 * doing Opens and Locks with reclaim. If these fail, it deletes the
1884 * corresponding state.
1885 */
1886static void
1887nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
1888{
1889	struct nfsclowner *owp, *nowp;
1890	struct nfsclopen *op, *nop;
1891	struct nfscllockowner *lp, *nlp;
1892	struct nfscllock *lop, *nlop;
1893	struct nfscldeleg *dp, *ndp, *tdp;
1894	struct nfsmount *nmp;
1895	struct ucred *tcred;
1896	struct nfsclopenhead extra_open;
1897	struct nfscldeleghead extra_deleg;
1898	struct nfsreq *rep;
1899	u_int64_t len;
1900	u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
1901	int i, igotlock = 0, error, trycnt, firstlock;
1902	struct nfscllayout *lyp, *nlyp;
1903
1904	/*
1905	 * First, lock the client structure, so everyone else will
1906	 * block when trying to use state.
1907	 */
1908	NFSLOCKCLSTATE();
1909	clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
1910	do {
1911		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1912		    NFSCLSTATEMUTEXPTR, NULL);
1913	} while (!igotlock);
1914	NFSUNLOCKCLSTATE();
1915
1916	nmp = clp->nfsc_nmp;
1917	if (nmp == NULL)
1918		panic("nfscl recover");
1919
1920	/*
1921	 * For now, just get rid of all layouts. There may be a need
1922	 * to do LayoutCommit Ops with reclaim == true later.
1923	 */
1924	TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp)
1925		nfscl_freelayout(lyp);
1926	TAILQ_INIT(&clp->nfsc_layout);
1927	for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++)
1928		LIST_INIT(&clp->nfsc_layouthash[i]);
1929
1930	trycnt = 5;
1931	do {
1932		error = nfsrpc_setclient(nmp, clp, 1, cred, p);
1933	} while ((error == NFSERR_STALECLIENTID ||
1934	     error == NFSERR_BADSESSION ||
1935	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
1936	if (error) {
1937		nfscl_cleanclient(clp);
1938		NFSLOCKCLSTATE();
1939		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
1940		    NFSCLFLAGS_RECOVER | NFSCLFLAGS_RECVRINPROG);
1941		wakeup(&clp->nfsc_flags);
1942		nfsv4_unlock(&clp->nfsc_lock, 0);
1943		NFSUNLOCKCLSTATE();
1944		return;
1945	}
1946	clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
1947	clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
1948
1949	/*
1950	 * Mark requests already queued on the server, so that they don't
1951	 * initiate another recovery cycle. Any requests already in the
1952	 * queue that handle state information will have the old stale
1953	 * clientid/stateid and will get a NFSERR_STALESTATEID,
1954	 * NFSERR_STALECLIENTID or NFSERR_BADSESSION reply from the server.
1955	 * This will be translated to NFSERR_STALEDONTRECOVER when
1956	 * R_DONTRECOVER is set.
1957	 */
1958	NFSLOCKREQ();
1959	TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) {
1960		if (rep->r_nmp == nmp)
1961			rep->r_flags |= R_DONTRECOVER;
1962	}
1963	NFSUNLOCKREQ();
1964
1965	/*
1966	 * Now, mark all delegations "need reclaim".
1967	 */
1968	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
1969		dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM;
1970
1971	TAILQ_INIT(&extra_deleg);
1972	LIST_INIT(&extra_open);
1973	/*
1974	 * Now traverse the state lists, doing Open and Lock Reclaims.
1975	 */
1976	tcred = newnfs_getcred();
1977	owp = LIST_FIRST(&clp->nfsc_owner);
1978	while (owp != NULL) {
1979	    nowp = LIST_NEXT(owp, nfsow_list);
1980	    owp->nfsow_seqid = 0;
1981	    op = LIST_FIRST(&owp->nfsow_open);
1982	    while (op != NULL) {
1983		nop = LIST_NEXT(op, nfso_list);
1984		if (error != NFSERR_NOGRACE) {
1985		    /* Search for a delegation to reclaim with the open */
1986		    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1987			if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1988			    continue;
1989			if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
1990			    mode = NFSV4OPEN_ACCESSWRITE;
1991			    delegtype = NFSV4OPEN_DELEGATEWRITE;
1992			} else {
1993			    mode = NFSV4OPEN_ACCESSREAD;
1994			    delegtype = NFSV4OPEN_DELEGATEREAD;
1995			}
1996			if ((op->nfso_mode & mode) == mode &&
1997			    op->nfso_fhlen == dp->nfsdl_fhlen &&
1998			    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen))
1999			    break;
2000		    }
2001		    ndp = dp;
2002		    if (dp == NULL)
2003			delegtype = NFSV4OPEN_DELEGATENONE;
2004		    newnfs_copycred(&op->nfso_cred, tcred);
2005		    error = nfscl_tryopen(nmp, NULL, op->nfso_fh,
2006			op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen,
2007			op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
2008			tcred, p);
2009		    if (!error) {
2010			/* Handle any replied delegation */
2011			if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
2012			    || NFSMNT_RDONLY(nmp->nm_mountp))) {
2013			    if ((ndp->nfsdl_flags & NFSCLDL_WRITE))
2014				mode = NFSV4OPEN_ACCESSWRITE;
2015			    else
2016				mode = NFSV4OPEN_ACCESSREAD;
2017			    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2018				if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
2019				    continue;
2020				if ((op->nfso_mode & mode) == mode &&
2021				    op->nfso_fhlen == dp->nfsdl_fhlen &&
2022				    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh,
2023				    op->nfso_fhlen)) {
2024				    dp->nfsdl_stateid = ndp->nfsdl_stateid;
2025				    dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit;
2026				    dp->nfsdl_ace = ndp->nfsdl_ace;
2027				    dp->nfsdl_change = ndp->nfsdl_change;
2028				    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2029				    if ((ndp->nfsdl_flags & NFSCLDL_RECALL))
2030					dp->nfsdl_flags |= NFSCLDL_RECALL;
2031				    FREE((caddr_t)ndp, M_NFSCLDELEG);
2032				    ndp = NULL;
2033				    break;
2034				}
2035			    }
2036			}
2037			if (ndp != NULL)
2038			    TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list);
2039
2040			/* and reclaim all byte range locks */
2041			lp = LIST_FIRST(&op->nfso_lock);
2042			while (lp != NULL) {
2043			    nlp = LIST_NEXT(lp, nfsl_list);
2044			    lp->nfsl_seqid = 0;
2045			    firstlock = 1;
2046			    lop = LIST_FIRST(&lp->nfsl_lock);
2047			    while (lop != NULL) {
2048				nlop = LIST_NEXT(lop, nfslo_list);
2049				if (lop->nfslo_end == NFS64BITSSET)
2050				    len = NFS64BITSSET;
2051				else
2052				    len = lop->nfslo_end - lop->nfslo_first;
2053				if (error != NFSERR_NOGRACE)
2054				    error = nfscl_trylock(nmp, NULL,
2055					op->nfso_fh, op->nfso_fhlen, lp,
2056					firstlock, 1, lop->nfslo_first, len,
2057					lop->nfslo_type, tcred, p);
2058				if (error != 0)
2059				    nfscl_freelock(lop, 0);
2060				else
2061				    firstlock = 0;
2062				lop = nlop;
2063			    }
2064			    /* If no locks, but a lockowner, just delete it. */
2065			    if (LIST_EMPTY(&lp->nfsl_lock))
2066				nfscl_freelockowner(lp, 0);
2067			    lp = nlp;
2068			}
2069		    } else {
2070			nfscl_freeopen(op, 0);
2071		    }
2072		}
2073		op = nop;
2074	    }
2075	    owp = nowp;
2076	}
2077
2078	/*
2079	 * Now, try and get any delegations not yet reclaimed by cobbling
2080	 * to-gether an appropriate open.
2081	 */
2082	nowp = NULL;
2083	dp = TAILQ_FIRST(&clp->nfsc_deleg);
2084	while (dp != NULL) {
2085	    ndp = TAILQ_NEXT(dp, nfsdl_list);
2086	    if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) {
2087		if (nowp == NULL) {
2088		    MALLOC(nowp, struct nfsclowner *,
2089			sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK);
2090		    /*
2091		     * Name must be as long an largest possible
2092		     * NFSV4CL_LOCKNAMELEN. 12 for now.
2093		     */
2094		    NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner,
2095			NFSV4CL_LOCKNAMELEN);
2096		    LIST_INIT(&nowp->nfsow_open);
2097		    nowp->nfsow_clp = clp;
2098		    nowp->nfsow_seqid = 0;
2099		    nowp->nfsow_defunct = 0;
2100		    nfscl_lockinit(&nowp->nfsow_rwlock);
2101		}
2102		nop = NULL;
2103		if (error != NFSERR_NOGRACE) {
2104		    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
2105			dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
2106		    nop->nfso_own = nowp;
2107		    if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
2108			nop->nfso_mode = NFSV4OPEN_ACCESSWRITE;
2109			delegtype = NFSV4OPEN_DELEGATEWRITE;
2110		    } else {
2111			nop->nfso_mode = NFSV4OPEN_ACCESSREAD;
2112			delegtype = NFSV4OPEN_DELEGATEREAD;
2113		    }
2114		    nop->nfso_opencnt = 0;
2115		    nop->nfso_posixlock = 1;
2116		    nop->nfso_fhlen = dp->nfsdl_fhlen;
2117		    NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen);
2118		    LIST_INIT(&nop->nfso_lock);
2119		    nop->nfso_stateid.seqid = 0;
2120		    nop->nfso_stateid.other[0] = 0;
2121		    nop->nfso_stateid.other[1] = 0;
2122		    nop->nfso_stateid.other[2] = 0;
2123		    newnfs_copycred(&dp->nfsdl_cred, tcred);
2124		    newnfs_copyincred(tcred, &nop->nfso_cred);
2125		    tdp = NULL;
2126		    error = nfscl_tryopen(nmp, NULL, nop->nfso_fh,
2127			nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen,
2128			nop->nfso_mode, nop, NULL, 0, &tdp, 1,
2129			delegtype, tcred, p);
2130		    if (tdp != NULL) {
2131			if ((tdp->nfsdl_flags & NFSCLDL_WRITE))
2132			    mode = NFSV4OPEN_ACCESSWRITE;
2133			else
2134			    mode = NFSV4OPEN_ACCESSREAD;
2135			if ((nop->nfso_mode & mode) == mode &&
2136			    nop->nfso_fhlen == tdp->nfsdl_fhlen &&
2137			    !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh,
2138			    nop->nfso_fhlen)) {
2139			    dp->nfsdl_stateid = tdp->nfsdl_stateid;
2140			    dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit;
2141			    dp->nfsdl_ace = tdp->nfsdl_ace;
2142			    dp->nfsdl_change = tdp->nfsdl_change;
2143			    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2144			    if ((tdp->nfsdl_flags & NFSCLDL_RECALL))
2145				dp->nfsdl_flags |= NFSCLDL_RECALL;
2146			    FREE((caddr_t)tdp, M_NFSCLDELEG);
2147			} else {
2148			    TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list);
2149			}
2150		    }
2151		}
2152		if (error) {
2153		    if (nop != NULL)
2154			FREE((caddr_t)nop, M_NFSCLOPEN);
2155		    /*
2156		     * Couldn't reclaim it, so throw the state
2157		     * away. Ouch!!
2158		     */
2159		    nfscl_cleandeleg(dp);
2160		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
2161		} else {
2162		    LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
2163		}
2164	    }
2165	    dp = ndp;
2166	}
2167
2168	/*
2169	 * Now, get rid of extra Opens and Delegations.
2170	 */
2171	LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) {
2172		do {
2173			newnfs_copycred(&op->nfso_cred, tcred);
2174			error = nfscl_tryclose(op, tcred, nmp, p);
2175			if (error == NFSERR_GRACE)
2176				(void) nfs_catnap(PZERO, error, "nfsexcls");
2177		} while (error == NFSERR_GRACE);
2178		LIST_REMOVE(op, nfso_list);
2179		FREE((caddr_t)op, M_NFSCLOPEN);
2180	}
2181	if (nowp != NULL)
2182		FREE((caddr_t)nowp, M_NFSCLOWNER);
2183
2184	TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) {
2185		do {
2186			newnfs_copycred(&dp->nfsdl_cred, tcred);
2187			error = nfscl_trydelegreturn(dp, tcred, nmp, p);
2188			if (error == NFSERR_GRACE)
2189				(void) nfs_catnap(PZERO, error, "nfsexdlg");
2190		} while (error == NFSERR_GRACE);
2191		TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list);
2192		FREE((caddr_t)dp, M_NFSCLDELEG);
2193	}
2194
2195	/* For NFSv4.1 or later, do a RECLAIM_COMPLETE. */
2196	if (NFSHASNFSV4N(nmp))
2197		(void)nfsrpc_reclaimcomplete(nmp, cred, p);
2198
2199	NFSLOCKCLSTATE();
2200	clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG;
2201	wakeup(&clp->nfsc_flags);
2202	nfsv4_unlock(&clp->nfsc_lock, 0);
2203	NFSUNLOCKCLSTATE();
2204	NFSFREECRED(tcred);
2205}
2206
2207/*
2208 * This function is called when a server replies with NFSERR_EXPIRED.
2209 * It deletes all state for the client and does a fresh SetClientId/confirm.
2210 * XXX Someday it should post a signal to the process(es) that hold the
2211 * state, so they know that lock state has been lost.
2212 */
2213APPLESTATIC int
2214nfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
2215{
2216	struct nfsmount *nmp;
2217	struct ucred *cred;
2218	int igotlock = 0, error, trycnt;
2219
2220	/*
2221	 * If the clientid has gone away or a new SetClientid has already
2222	 * been done, just return ok.
2223	 */
2224	if (clp == NULL || clidrev != clp->nfsc_clientidrev)
2225		return (0);
2226
2227	/*
2228	 * First, lock the client structure, so everyone else will
2229	 * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so
2230	 * that only one thread does the work.
2231	 */
2232	NFSLOCKCLSTATE();
2233	clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
2234	do {
2235		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2236		    NFSCLSTATEMUTEXPTR, NULL);
2237	} while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
2238	if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
2239		if (igotlock)
2240			nfsv4_unlock(&clp->nfsc_lock, 0);
2241		NFSUNLOCKCLSTATE();
2242		return (0);
2243	}
2244	clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
2245	NFSUNLOCKCLSTATE();
2246
2247	nmp = clp->nfsc_nmp;
2248	if (nmp == NULL)
2249		panic("nfscl expired");
2250	cred = newnfs_getcred();
2251	trycnt = 5;
2252	do {
2253		error = nfsrpc_setclient(nmp, clp, 0, cred, p);
2254	} while ((error == NFSERR_STALECLIENTID ||
2255	     error == NFSERR_BADSESSION ||
2256	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
2257	if (error) {
2258		/*
2259		 * Clear out any state.
2260		 */
2261		nfscl_cleanclient(clp);
2262		NFSLOCKCLSTATE();
2263		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
2264		    NFSCLFLAGS_RECOVER);
2265	} else {
2266		/*
2267		 * Expire the state for the client.
2268		 */
2269		nfscl_expireclient(clp, nmp, cred, p);
2270		NFSLOCKCLSTATE();
2271		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
2272		clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2273	}
2274	clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG);
2275	wakeup(&clp->nfsc_flags);
2276	nfsv4_unlock(&clp->nfsc_lock, 0);
2277	NFSUNLOCKCLSTATE();
2278	NFSFREECRED(cred);
2279	return (error);
2280}
2281
2282/*
2283 * This function inserts a lock in the list after insert_lop.
2284 */
2285static void
2286nfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop,
2287    struct nfscllock *insert_lop, int local)
2288{
2289
2290	if ((struct nfscllockowner *)insert_lop == lp)
2291		LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list);
2292	else
2293		LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list);
2294	if (local)
2295		newnfsstats.cllocallocks++;
2296	else
2297		newnfsstats.cllocks++;
2298}
2299
2300/*
2301 * This function updates the locking for a lock owner and given file. It
2302 * maintains a list of lock ranges ordered on increasing file offset that
2303 * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style).
2304 * It always adds new_lop to the list and sometimes uses the one pointed
2305 * at by other_lopp.
2306 * Returns 1 if the locks were modified, 0 otherwise.
2307 */
2308static int
2309nfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp,
2310    struct nfscllock **other_lopp, int local)
2311{
2312	struct nfscllock *new_lop = *new_lopp;
2313	struct nfscllock *lop, *tlop, *ilop;
2314	struct nfscllock *other_lop;
2315	int unlock = 0, modified = 0;
2316	u_int64_t tmp;
2317
2318	/*
2319	 * Work down the list until the lock is merged.
2320	 */
2321	if (new_lop->nfslo_type == F_UNLCK)
2322		unlock = 1;
2323	ilop = (struct nfscllock *)lp;
2324	lop = LIST_FIRST(&lp->nfsl_lock);
2325	while (lop != NULL) {
2326	    /*
2327	     * Only check locks for this file that aren't before the start of
2328	     * new lock's range.
2329	     */
2330	    if (lop->nfslo_end >= new_lop->nfslo_first) {
2331		if (new_lop->nfslo_end < lop->nfslo_first) {
2332		    /*
2333		     * If the new lock ends before the start of the
2334		     * current lock's range, no merge, just insert
2335		     * the new lock.
2336		     */
2337		    break;
2338		}
2339		if (new_lop->nfslo_type == lop->nfslo_type ||
2340		    (new_lop->nfslo_first <= lop->nfslo_first &&
2341		     new_lop->nfslo_end >= lop->nfslo_end)) {
2342		    /*
2343		     * This lock can be absorbed by the new lock/unlock.
2344		     * This happens when it covers the entire range
2345		     * of the old lock or is contiguous
2346		     * with the old lock and is of the same type or an
2347		     * unlock.
2348		     */
2349		    if (new_lop->nfslo_type != lop->nfslo_type ||
2350			new_lop->nfslo_first != lop->nfslo_first ||
2351			new_lop->nfslo_end != lop->nfslo_end)
2352			modified = 1;
2353		    if (lop->nfslo_first < new_lop->nfslo_first)
2354			new_lop->nfslo_first = lop->nfslo_first;
2355		    if (lop->nfslo_end > new_lop->nfslo_end)
2356			new_lop->nfslo_end = lop->nfslo_end;
2357		    tlop = lop;
2358		    lop = LIST_NEXT(lop, nfslo_list);
2359		    nfscl_freelock(tlop, local);
2360		    continue;
2361		}
2362
2363		/*
2364		 * All these cases are for contiguous locks that are not the
2365		 * same type, so they can't be merged.
2366		 */
2367		if (new_lop->nfslo_first <= lop->nfslo_first) {
2368		    /*
2369		     * This case is where the new lock overlaps with the
2370		     * first part of the old lock. Move the start of the
2371		     * old lock to just past the end of the new lock. The
2372		     * new lock will be inserted in front of the old, since
2373		     * ilop hasn't been updated. (We are done now.)
2374		     */
2375		    if (lop->nfslo_first != new_lop->nfslo_end) {
2376			lop->nfslo_first = new_lop->nfslo_end;
2377			modified = 1;
2378		    }
2379		    break;
2380		}
2381		if (new_lop->nfslo_end >= lop->nfslo_end) {
2382		    /*
2383		     * This case is where the new lock overlaps with the
2384		     * end of the old lock's range. Move the old lock's
2385		     * end to just before the new lock's first and insert
2386		     * the new lock after the old lock.
2387		     * Might not be done yet, since the new lock could
2388		     * overlap further locks with higher ranges.
2389		     */
2390		    if (lop->nfslo_end != new_lop->nfslo_first) {
2391			lop->nfslo_end = new_lop->nfslo_first;
2392			modified = 1;
2393		    }
2394		    ilop = lop;
2395		    lop = LIST_NEXT(lop, nfslo_list);
2396		    continue;
2397		}
2398		/*
2399		 * The final case is where the new lock's range is in the
2400		 * middle of the current lock's and splits the current lock
2401		 * up. Use *other_lopp to handle the second part of the
2402		 * split old lock range. (We are done now.)
2403		 * For unlock, we use new_lop as other_lop and tmp, since
2404		 * other_lop and new_lop are the same for this case.
2405		 * We noted the unlock case above, so we don't need
2406		 * new_lop->nfslo_type any longer.
2407		 */
2408		tmp = new_lop->nfslo_first;
2409		if (unlock) {
2410		    other_lop = new_lop;
2411		    *new_lopp = NULL;
2412		} else {
2413		    other_lop = *other_lopp;
2414		    *other_lopp = NULL;
2415		}
2416		other_lop->nfslo_first = new_lop->nfslo_end;
2417		other_lop->nfslo_end = lop->nfslo_end;
2418		other_lop->nfslo_type = lop->nfslo_type;
2419		lop->nfslo_end = tmp;
2420		nfscl_insertlock(lp, other_lop, lop, local);
2421		ilop = lop;
2422		modified = 1;
2423		break;
2424	    }
2425	    ilop = lop;
2426	    lop = LIST_NEXT(lop, nfslo_list);
2427	    if (lop == NULL)
2428		break;
2429	}
2430
2431	/*
2432	 * Insert the new lock in the list at the appropriate place.
2433	 */
2434	if (!unlock) {
2435		nfscl_insertlock(lp, new_lop, ilop, local);
2436		*new_lopp = NULL;
2437		modified = 1;
2438	}
2439	return (modified);
2440}
2441
2442/*
2443 * This function must be run as a kernel thread.
2444 * It does Renew Ops and recovery, when required.
2445 */
2446APPLESTATIC void
2447nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
2448{
2449	struct nfsclowner *owp, *nowp;
2450	struct nfsclopen *op;
2451	struct nfscllockowner *lp, *nlp;
2452	struct nfscldeleghead dh;
2453	struct nfscldeleg *dp, *ndp;
2454	struct ucred *cred;
2455	u_int32_t clidrev;
2456	int error, cbpathdown, islept, igotlock, ret, clearok;
2457	uint32_t recover_done_time = 0;
2458	time_t mytime;
2459	static time_t prevsec = 0;
2460	struct nfscllockownerfh *lfhp, *nlfhp;
2461	struct nfscllockownerfhhead lfh;
2462	struct nfscllayout *lyp, *nlyp;
2463	struct nfscldevinfo *dip, *ndip;
2464	struct nfscllayouthead rlh;
2465	struct nfsclrecalllayout *recallp;
2466	struct nfsclds *dsp;
2467
2468	cred = newnfs_getcred();
2469	NFSLOCKCLSTATE();
2470	clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD;
2471	NFSUNLOCKCLSTATE();
2472	for(;;) {
2473		newnfs_setroot(cred);
2474		cbpathdown = 0;
2475		if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) {
2476			/*
2477			 * Only allow one recover within 1/2 of the lease
2478			 * duration (nfsc_renew).
2479			 */
2480			if (recover_done_time < NFSD_MONOSEC) {
2481				recover_done_time = NFSD_MONOSEC +
2482				    clp->nfsc_renew;
2483				NFSCL_DEBUG(1, "Doing recovery..\n");
2484				nfscl_recover(clp, cred, p);
2485			} else {
2486				NFSCL_DEBUG(1, "Clear Recovery dt=%u ms=%jd\n",
2487				    recover_done_time, (intmax_t)NFSD_MONOSEC);
2488				NFSLOCKCLSTATE();
2489				clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2490				NFSUNLOCKCLSTATE();
2491			}
2492		}
2493		if (clp->nfsc_expire <= NFSD_MONOSEC &&
2494		    (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
2495			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
2496			clidrev = clp->nfsc_clientidrev;
2497			error = nfsrpc_renew(clp, NULL, cred, p);
2498			if (error == NFSERR_CBPATHDOWN)
2499			    cbpathdown = 1;
2500			else if (error == NFSERR_STALECLIENTID ||
2501			    error == NFSERR_BADSESSION) {
2502			    NFSLOCKCLSTATE();
2503			    clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2504			    NFSUNLOCKCLSTATE();
2505			} else if (error == NFSERR_EXPIRED)
2506			    (void) nfscl_hasexpired(clp, clidrev, p);
2507		}
2508
2509checkdsrenew:
2510		if (NFSHASNFSV4N(clp->nfsc_nmp)) {
2511			/* Do renews for any DS sessions. */
2512			NFSLOCKMNT(clp->nfsc_nmp);
2513			/* Skip first entry, since the MDS is handled above. */
2514			dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess);
2515			if (dsp != NULL)
2516				dsp = TAILQ_NEXT(dsp, nfsclds_list);
2517			while (dsp != NULL) {
2518				if (dsp->nfsclds_expire <= NFSD_MONOSEC &&
2519				    dsp->nfsclds_sess.nfsess_defunct == 0) {
2520					dsp->nfsclds_expire = NFSD_MONOSEC +
2521					    clp->nfsc_renew;
2522					NFSUNLOCKMNT(clp->nfsc_nmp);
2523					(void)nfsrpc_renew(clp, dsp, cred, p);
2524					goto checkdsrenew;
2525				}
2526				dsp = TAILQ_NEXT(dsp, nfsclds_list);
2527			}
2528			NFSUNLOCKMNT(clp->nfsc_nmp);
2529		}
2530
2531		TAILQ_INIT(&dh);
2532		NFSLOCKCLSTATE();
2533		if (cbpathdown)
2534			/* It's a Total Recall! */
2535			nfscl_totalrecall(clp);
2536
2537		/*
2538		 * Now, handle defunct owners.
2539		 */
2540		LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
2541			if (LIST_EMPTY(&owp->nfsow_open)) {
2542				if (owp->nfsow_defunct != 0)
2543					nfscl_freeopenowner(owp, 0);
2544			}
2545		}
2546
2547		/*
2548		 * Do the recall on any delegations. To avoid trouble, always
2549		 * come back up here after having slept.
2550		 */
2551		igotlock = 0;
2552tryagain:
2553		dp = TAILQ_FIRST(&clp->nfsc_deleg);
2554		while (dp != NULL) {
2555			ndp = TAILQ_NEXT(dp, nfsdl_list);
2556			if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
2557				/*
2558				 * Wait for outstanding I/O ops to be done.
2559				 */
2560				if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
2561				    if (igotlock) {
2562					nfsv4_unlock(&clp->nfsc_lock, 0);
2563					igotlock = 0;
2564				    }
2565				    dp->nfsdl_rwlock.nfslock_lock |=
2566					NFSV4LOCK_WANTED;
2567				    (void) nfsmsleep(&dp->nfsdl_rwlock,
2568					NFSCLSTATEMUTEXPTR, PZERO, "nfscld",
2569					NULL);
2570				    goto tryagain;
2571				}
2572				while (!igotlock) {
2573				    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
2574					&islept, NFSCLSTATEMUTEXPTR, NULL);
2575				    if (islept)
2576					goto tryagain;
2577				}
2578				NFSUNLOCKCLSTATE();
2579				newnfs_copycred(&dp->nfsdl_cred, cred);
2580				ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
2581				    NULL, cred, p, 1);
2582				if (!ret) {
2583				    nfscl_cleandeleg(dp);
2584				    TAILQ_REMOVE(&clp->nfsc_deleg, dp,
2585					nfsdl_list);
2586				    LIST_REMOVE(dp, nfsdl_hash);
2587				    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2588				    nfscl_delegcnt--;
2589				    newnfsstats.cldelegates--;
2590				}
2591				NFSLOCKCLSTATE();
2592			}
2593			dp = ndp;
2594		}
2595
2596		/*
2597		 * Clear out old delegations, if we are above the high water
2598		 * mark. Only clear out ones with no state related to them.
2599		 * The tailq list is in LRU order.
2600		 */
2601		dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
2602		while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) {
2603		    ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
2604		    if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
2605			dp->nfsdl_rwlock.nfslock_lock == 0 &&
2606			dp->nfsdl_timestamp < NFSD_MONOSEC &&
2607			(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
2608			  NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) {
2609			clearok = 1;
2610			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2611			    op = LIST_FIRST(&owp->nfsow_open);
2612			    if (op != NULL) {
2613				clearok = 0;
2614				break;
2615			    }
2616			}
2617			if (clearok) {
2618			    LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
2619				if (!LIST_EMPTY(&lp->nfsl_lock)) {
2620				    clearok = 0;
2621				    break;
2622				}
2623			    }
2624			}
2625			if (clearok) {
2626			    TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
2627			    LIST_REMOVE(dp, nfsdl_hash);
2628			    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2629			    nfscl_delegcnt--;
2630			    newnfsstats.cldelegates--;
2631			}
2632		    }
2633		    dp = ndp;
2634		}
2635		if (igotlock)
2636			nfsv4_unlock(&clp->nfsc_lock, 0);
2637
2638		/*
2639		 * Do the recall on any layouts. To avoid trouble, always
2640		 * come back up here after having slept.
2641		 */
2642		TAILQ_INIT(&rlh);
2643tryagain2:
2644		TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) {
2645			if ((lyp->nfsly_flags & NFSLY_RECALL) != 0) {
2646				/*
2647				 * Wait for outstanding I/O ops to be done.
2648				 */
2649				if (lyp->nfsly_lock.nfslock_usecnt > 0 ||
2650				    (lyp->nfsly_lock.nfslock_lock &
2651				     NFSV4LOCK_LOCK) != 0) {
2652					lyp->nfsly_lock.nfslock_lock |=
2653					    NFSV4LOCK_WANTED;
2654					(void)nfsmsleep(&lyp->nfsly_lock,
2655					    NFSCLSTATEMUTEXPTR, PZERO, "nfslyp",
2656					    NULL);
2657					goto tryagain2;
2658				}
2659				/* Move the layout to the recall list. */
2660				TAILQ_REMOVE(&clp->nfsc_layout, lyp,
2661				    nfsly_list);
2662				LIST_REMOVE(lyp, nfsly_hash);
2663				TAILQ_INSERT_HEAD(&rlh, lyp, nfsly_list);
2664
2665				/* Handle any layout commits. */
2666				if (!NFSHASNOLAYOUTCOMMIT(clp->nfsc_nmp) &&
2667				    (lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
2668					lyp->nfsly_flags &= ~NFSLY_WRITTEN;
2669					NFSUNLOCKCLSTATE();
2670					NFSCL_DEBUG(3, "do layoutcommit\n");
2671					nfscl_dolayoutcommit(clp->nfsc_nmp, lyp,
2672					    cred, p);
2673					NFSLOCKCLSTATE();
2674					goto tryagain2;
2675				}
2676			}
2677		}
2678
2679		/* Now, look for stale layouts. */
2680		lyp = TAILQ_LAST(&clp->nfsc_layout, nfscllayouthead);
2681		while (lyp != NULL) {
2682			nlyp = TAILQ_PREV(lyp, nfscllayouthead, nfsly_list);
2683			if (lyp->nfsly_timestamp < NFSD_MONOSEC &&
2684			    (lyp->nfsly_flags & NFSLY_RECALL) == 0 &&
2685			    lyp->nfsly_lock.nfslock_usecnt == 0 &&
2686			    lyp->nfsly_lock.nfslock_lock == 0) {
2687				NFSCL_DEBUG(4, "ret stale lay=%d\n",
2688				    nfscl_layoutcnt);
2689				recallp = malloc(sizeof(*recallp),
2690				    M_NFSLAYRECALL, M_NOWAIT);
2691				if (recallp == NULL)
2692					break;
2693				(void)nfscl_layoutrecall(NFSLAYOUTRETURN_FILE,
2694				    lyp, NFSLAYOUTIOMODE_ANY, 0, UINT64_MAX,
2695				    lyp->nfsly_stateid.seqid, recallp);
2696			}
2697			lyp = nlyp;
2698		}
2699
2700		/*
2701		 * Free up any unreferenced device info structures.
2702		 */
2703		LIST_FOREACH_SAFE(dip, &clp->nfsc_devinfo, nfsdi_list, ndip) {
2704			if (dip->nfsdi_layoutrefs == 0 &&
2705			    dip->nfsdi_refcnt == 0) {
2706				NFSCL_DEBUG(4, "freeing devinfo\n");
2707				LIST_REMOVE(dip, nfsdi_list);
2708				nfscl_freedevinfo(dip);
2709			}
2710		}
2711		NFSUNLOCKCLSTATE();
2712
2713		/* Do layout return(s), as required. */
2714		TAILQ_FOREACH_SAFE(lyp, &rlh, nfsly_list, nlyp) {
2715			TAILQ_REMOVE(&rlh, lyp, nfsly_list);
2716			NFSCL_DEBUG(4, "ret layout\n");
2717			nfscl_layoutreturn(clp->nfsc_nmp, lyp, cred, p);
2718			nfscl_freelayout(lyp);
2719		}
2720
2721		/*
2722		 * Delegreturn any delegations cleaned out or recalled.
2723		 */
2724		TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
2725			newnfs_copycred(&dp->nfsdl_cred, cred);
2726			(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2727			TAILQ_REMOVE(&dh, dp, nfsdl_list);
2728			FREE((caddr_t)dp, M_NFSCLDELEG);
2729		}
2730
2731		SLIST_INIT(&lfh);
2732		/*
2733		 * Call nfscl_cleanupkext() once per second to check for
2734		 * open/lock owners where the process has exited.
2735		 */
2736		mytime = NFSD_MONOSEC;
2737		if (prevsec != mytime) {
2738			prevsec = mytime;
2739			nfscl_cleanupkext(clp, &lfh);
2740		}
2741
2742		/*
2743		 * Do a ReleaseLockOwner for all lock owners where the
2744		 * associated process no longer exists, as found by
2745		 * nfscl_cleanupkext().
2746		 */
2747		newnfs_setroot(cred);
2748		SLIST_FOREACH_SAFE(lfhp, &lfh, nfslfh_list, nlfhp) {
2749			LIST_FOREACH_SAFE(lp, &lfhp->nfslfh_lock, nfsl_list,
2750			    nlp) {
2751				(void)nfsrpc_rellockown(clp->nfsc_nmp, lp,
2752				    lfhp->nfslfh_fh, lfhp->nfslfh_len, cred,
2753				    p);
2754				nfscl_freelockowner(lp, 0);
2755			}
2756			free(lfhp, M_TEMP);
2757		}
2758		SLIST_INIT(&lfh);
2759
2760		NFSLOCKCLSTATE();
2761		if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
2762			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl",
2763			    hz);
2764		if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
2765			clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
2766			NFSUNLOCKCLSTATE();
2767			NFSFREECRED(cred);
2768			wakeup((caddr_t)clp);
2769			return;
2770		}
2771		NFSUNLOCKCLSTATE();
2772	}
2773}
2774
2775/*
2776 * Initiate state recovery. Called when NFSERR_STALECLIENTID,
2777 * NFSERR_STALESTATEID or NFSERR_BADSESSION is received.
2778 */
2779APPLESTATIC void
2780nfscl_initiate_recovery(struct nfsclclient *clp)
2781{
2782
2783	if (clp == NULL)
2784		return;
2785	NFSLOCKCLSTATE();
2786	clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2787	NFSUNLOCKCLSTATE();
2788	wakeup((caddr_t)clp);
2789}
2790
2791/*
2792 * Dump out the state stuff for debugging.
2793 */
2794APPLESTATIC void
2795nfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
2796    int lockowner, int locks)
2797{
2798	struct nfsclclient *clp;
2799	struct nfsclowner *owp;
2800	struct nfsclopen *op;
2801	struct nfscllockowner *lp;
2802	struct nfscllock *lop;
2803	struct nfscldeleg *dp;
2804
2805	clp = nmp->nm_clp;
2806	if (clp == NULL) {
2807		printf("nfscl dumpstate NULL clp\n");
2808		return;
2809	}
2810	NFSLOCKCLSTATE();
2811	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2812	  LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2813	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2814		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2815		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2816		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2817		    owp->nfsow_seqid);
2818	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2819		if (opens)
2820		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2821			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2822			op->nfso_stateid.other[2], op->nfso_opencnt,
2823			op->nfso_fh[12]);
2824		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2825		    if (lockowner)
2826			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2827			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2828			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2829			    lp->nfsl_seqid,
2830			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2831			    lp->nfsl_stateid.other[2]);
2832		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2833			if (locks)
2834#ifdef __FreeBSD__
2835			    printf("lck typ=%d fst=%ju end=%ju\n",
2836				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2837				(intmax_t)lop->nfslo_end);
2838#else
2839			    printf("lck typ=%d fst=%qd end=%qd\n",
2840				lop->nfslo_type, lop->nfslo_first,
2841				lop->nfslo_end);
2842#endif
2843		    }
2844		}
2845	    }
2846	  }
2847	}
2848	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2849	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2850		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2851		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2852		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2853		    owp->nfsow_seqid);
2854	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2855		if (opens)
2856		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2857			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2858			op->nfso_stateid.other[2], op->nfso_opencnt,
2859			op->nfso_fh[12]);
2860		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2861		    if (lockowner)
2862			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2863			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2864			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2865			    lp->nfsl_seqid,
2866			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2867			    lp->nfsl_stateid.other[2]);
2868		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2869			if (locks)
2870#ifdef __FreeBSD__
2871			    printf("lck typ=%d fst=%ju end=%ju\n",
2872				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2873				(intmax_t)lop->nfslo_end);
2874#else
2875			    printf("lck typ=%d fst=%qd end=%qd\n",
2876				lop->nfslo_type, lop->nfslo_first,
2877				lop->nfslo_end);
2878#endif
2879		    }
2880		}
2881	    }
2882	}
2883	NFSUNLOCKCLSTATE();
2884}
2885
2886/*
2887 * Check for duplicate open owners and opens.
2888 * (Only used as a diagnostic aid.)
2889 */
2890APPLESTATIC void
2891nfscl_dupopen(vnode_t vp, int dupopens)
2892{
2893	struct nfsclclient *clp;
2894	struct nfsclowner *owp, *owp2;
2895	struct nfsclopen *op, *op2;
2896	struct nfsfh *nfhp;
2897
2898	clp = VFSTONFS(vnode_mount(vp))->nm_clp;
2899	if (clp == NULL) {
2900		printf("nfscl dupopen NULL clp\n");
2901		return;
2902	}
2903	nfhp = VTONFS(vp)->n_fhp;
2904	NFSLOCKCLSTATE();
2905
2906	/*
2907	 * First, search for duplicate owners.
2908	 * These should never happen!
2909	 */
2910	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2911	    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2912		if (owp != owp2 &&
2913		    !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
2914		    NFSV4CL_LOCKNAMELEN)) {
2915			NFSUNLOCKCLSTATE();
2916			printf("DUP OWNER\n");
2917			nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0);
2918			return;
2919		}
2920	    }
2921	}
2922
2923	/*
2924	 * Now, search for duplicate stateids.
2925	 * These shouldn't happen, either.
2926	 */
2927	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2928	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2929		LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2930		    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2931			if (op != op2 &&
2932			    (op->nfso_stateid.other[0] != 0 ||
2933			     op->nfso_stateid.other[1] != 0 ||
2934			     op->nfso_stateid.other[2] != 0) &&
2935			    op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
2936			    op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
2937			    op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
2938			    NFSUNLOCKCLSTATE();
2939			    printf("DUP STATEID\n");
2940			    nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0,
2941				0);
2942			    return;
2943			}
2944		    }
2945		}
2946	    }
2947	}
2948
2949	/*
2950	 * Now search for duplicate opens.
2951	 * Duplicate opens for the same owner
2952	 * should never occur. Other duplicates are
2953	 * possible and are checked for if "dupopens"
2954	 * is true.
2955	 */
2956	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2957	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2958		if (nfhp->nfh_len == op2->nfso_fhlen &&
2959		    !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
2960		    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2961			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2962			    if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
2963				!NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
2964				(!NFSBCMP(op->nfso_own->nfsow_owner,
2965				 op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
2966				 dupopens)) {
2967				if (!NFSBCMP(op->nfso_own->nfsow_owner,
2968				    op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
2969				    NFSUNLOCKCLSTATE();
2970				    printf("BADDUP OPEN\n");
2971				} else {
2972				    NFSUNLOCKCLSTATE();
2973				    printf("DUP OPEN\n");
2974				}
2975				nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1,
2976				    0, 0);
2977				return;
2978			    }
2979			}
2980		    }
2981		}
2982	    }
2983	}
2984	NFSUNLOCKCLSTATE();
2985}
2986
2987/*
2988 * During close, find an open that needs to be dereferenced and
2989 * dereference it. If there are no more opens for this file,
2990 * log a message to that effect.
2991 * Opens aren't actually Close'd until VOP_INACTIVE() is performed
2992 * on the file's vnode.
2993 * This is the safe way, since it is difficult to identify
2994 * which open the close is for and I/O can be performed after the
2995 * close(2) system call when a file is mmap'd.
2996 * If it returns 0 for success, there will be a referenced
2997 * clp returned via clpp.
2998 */
2999APPLESTATIC int
3000nfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
3001{
3002	struct nfsclclient *clp;
3003	struct nfsclowner *owp;
3004	struct nfsclopen *op;
3005	struct nfscldeleg *dp;
3006	struct nfsfh *nfhp;
3007	int error, notdecr;
3008
3009	error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp);
3010	if (error)
3011		return (error);
3012	*clpp = clp;
3013
3014	nfhp = VTONFS(vp)->n_fhp;
3015	notdecr = 1;
3016	NFSLOCKCLSTATE();
3017	/*
3018	 * First, look for one under a delegation that was locally issued
3019	 * and just decrement the opencnt for it. Since all my Opens against
3020	 * the server are DENY_NONE, I don't see a problem with hanging
3021	 * onto them. (It is much easier to use one of the extant Opens
3022	 * that I already have on the server when a Delegation is recalled
3023	 * than to do fresh Opens.) Someday, I might need to rethink this, but.
3024	 */
3025	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3026	if (dp != NULL) {
3027		LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3028			op = LIST_FIRST(&owp->nfsow_open);
3029			if (op != NULL) {
3030				/*
3031				 * Since a delegation is for a file, there
3032				 * should never be more than one open for
3033				 * each openowner.
3034				 */
3035				if (LIST_NEXT(op, nfso_list) != NULL)
3036					panic("nfscdeleg opens");
3037				if (notdecr && op->nfso_opencnt > 0) {
3038					notdecr = 0;
3039					op->nfso_opencnt--;
3040					break;
3041				}
3042			}
3043		}
3044	}
3045
3046	/* Now process the opens against the server. */
3047	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3048		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3049			if (op->nfso_fhlen == nfhp->nfh_len &&
3050			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3051			    nfhp->nfh_len)) {
3052				/* Found an open, decrement cnt if possible */
3053				if (notdecr && op->nfso_opencnt > 0) {
3054					notdecr = 0;
3055					op->nfso_opencnt--;
3056				}
3057				/*
3058				 * There are more opens, so just return.
3059				 */
3060				if (op->nfso_opencnt > 0) {
3061					NFSUNLOCKCLSTATE();
3062					return (0);
3063				}
3064			}
3065		}
3066	}
3067	NFSUNLOCKCLSTATE();
3068	if (notdecr)
3069		printf("nfscl: never fnd open\n");
3070	return (0);
3071}
3072
3073APPLESTATIC int
3074nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
3075{
3076	struct nfsclclient *clp;
3077	struct nfsclowner *owp, *nowp;
3078	struct nfsclopen *op;
3079	struct nfscldeleg *dp;
3080	struct nfsfh *nfhp;
3081	int error;
3082
3083	error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp);
3084	if (error)
3085		return (error);
3086	*clpp = clp;
3087
3088	nfhp = VTONFS(vp)->n_fhp;
3089	NFSLOCKCLSTATE();
3090	/*
3091	 * First get rid of the local Open structures, which should be no
3092	 * longer in use.
3093	 */
3094	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3095	if (dp != NULL) {
3096		LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
3097			op = LIST_FIRST(&owp->nfsow_open);
3098			if (op != NULL) {
3099				KASSERT((op->nfso_opencnt == 0),
3100				    ("nfscl: bad open cnt on deleg"));
3101				nfscl_freeopen(op, 1);
3102			}
3103			nfscl_freeopenowner(owp, 1);
3104		}
3105	}
3106
3107	/* Return any layouts marked return on close. */
3108	nfscl_retoncloselayout(clp, nfhp->nfh_fh, nfhp->nfh_len);
3109
3110	/* Now process the opens against the server. */
3111lookformore:
3112	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3113		op = LIST_FIRST(&owp->nfsow_open);
3114		while (op != NULL) {
3115			if (op->nfso_fhlen == nfhp->nfh_len &&
3116			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3117			    nfhp->nfh_len)) {
3118				/* Found an open, close it. */
3119				KASSERT((op->nfso_opencnt == 0),
3120				    ("nfscl: bad open cnt on server"));
3121				NFSUNLOCKCLSTATE();
3122				nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op,
3123				    p);
3124				NFSLOCKCLSTATE();
3125				goto lookformore;
3126			}
3127			op = LIST_NEXT(op, nfso_list);
3128		}
3129	}
3130	NFSUNLOCKCLSTATE();
3131	return (0);
3132}
3133
3134/*
3135 * Return all delegations on this client.
3136 * (Must be called with client sleep lock.)
3137 */
3138static void
3139nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
3140{
3141	struct nfscldeleg *dp, *ndp;
3142	struct ucred *cred;
3143
3144	cred = newnfs_getcred();
3145	TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
3146		nfscl_cleandeleg(dp);
3147		(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
3148		nfscl_freedeleg(&clp->nfsc_deleg, dp);
3149	}
3150	NFSFREECRED(cred);
3151}
3152
3153/*
3154 * Do a callback RPC.
3155 */
3156APPLESTATIC void
3157nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
3158{
3159	int clist, gotseq_ok, i, j, k, op, rcalls;
3160	u_int32_t *tl;
3161	struct nfsclclient *clp;
3162	struct nfscldeleg *dp = NULL;
3163	int numops, taglen = -1, error = 0, trunc;
3164	u_int32_t minorvers = 0, retops = 0, *retopsp = NULL, *repp, cbident;
3165	u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
3166	vnode_t vp = NULL;
3167	struct nfsnode *np;
3168	struct vattr va;
3169	struct nfsfh *nfhp;
3170	mount_t mp;
3171	nfsattrbit_t attrbits, rattrbits;
3172	nfsv4stateid_t stateid;
3173	uint32_t seqid, slotid = 0, highslot, cachethis;
3174	uint8_t sessionid[NFSX_V4SESSIONID];
3175	struct mbuf *rep;
3176	struct nfscllayout *lyp;
3177	uint64_t filesid[2], len, off;
3178	int changed, gotone, laytype, recalltype;
3179	uint32_t iomode;
3180	struct nfsclrecalllayout *recallp = NULL;
3181	struct nfsclsession *tsep;
3182
3183	gotseq_ok = 0;
3184	nfsrvd_rephead(nd);
3185	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3186	taglen = fxdr_unsigned(int, *tl);
3187	if (taglen < 0) {
3188		error = EBADRPC;
3189		goto nfsmout;
3190	}
3191	if (taglen <= NFSV4_SMALLSTR)
3192		tagstr = tag;
3193	else
3194		tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
3195	error = nfsrv_mtostr(nd, tagstr, taglen);
3196	if (error) {
3197		if (taglen > NFSV4_SMALLSTR)
3198			free(tagstr, M_TEMP);
3199		taglen = -1;
3200		goto nfsmout;
3201	}
3202	(void) nfsm_strtom(nd, tag, taglen);
3203	if (taglen > NFSV4_SMALLSTR) {
3204		free(tagstr, M_TEMP);
3205	}
3206	NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
3207	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3208	minorvers = fxdr_unsigned(u_int32_t, *tl++);
3209	if (minorvers != NFSV4_MINORVERSION && minorvers != NFSV41_MINORVERSION)
3210		nd->nd_repstat = NFSERR_MINORVERMISMATCH;
3211	cbident = fxdr_unsigned(u_int32_t, *tl++);
3212	if (nd->nd_repstat)
3213		numops = 0;
3214	else
3215		numops = fxdr_unsigned(int, *tl);
3216	/*
3217	 * Loop around doing the sub ops.
3218	 */
3219	for (i = 0; i < numops; i++) {
3220		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3221		NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
3222		*repp++ = *tl;
3223		op = fxdr_unsigned(int, *tl);
3224		if (op < NFSV4OP_CBGETATTR ||
3225		   (op > NFSV4OP_CBRECALL && minorvers == NFSV4_MINORVERSION) ||
3226		   (op > NFSV4OP_CBNOTIFYDEVID &&
3227		    minorvers == NFSV41_MINORVERSION)) {
3228		    nd->nd_repstat = NFSERR_OPILLEGAL;
3229		    *repp = nfscl_errmap(nd, minorvers);
3230		    retops++;
3231		    break;
3232		}
3233		nd->nd_procnum = op;
3234		if (op < NFSV4OP_CBNOPS)
3235			newnfsstats.cbrpccnt[nd->nd_procnum]++;
3236		switch (op) {
3237		case NFSV4OP_CBGETATTR:
3238			NFSCL_DEBUG(4, "cbgetattr\n");
3239			mp = NULL;
3240			vp = NULL;
3241			error = nfsm_getfh(nd, &nfhp);
3242			if (!error)
3243				error = nfsrv_getattrbits(nd, &attrbits,
3244				    NULL, NULL);
3245			if (error == 0 && i == 0 &&
3246			    minorvers != NFSV4_MINORVERSION)
3247				error = NFSERR_OPNOTINSESS;
3248			if (!error) {
3249				mp = nfscl_getmnt(minorvers, sessionid, cbident,
3250				    &clp);
3251				if (mp == NULL)
3252					error = NFSERR_SERVERFAULT;
3253			}
3254			if (!error) {
3255				error = nfscl_ngetreopen(mp, nfhp->nfh_fh,
3256				    nfhp->nfh_len, p, &np);
3257				if (!error)
3258					vp = NFSTOV(np);
3259			}
3260			if (!error) {
3261				NFSZERO_ATTRBIT(&rattrbits);
3262				NFSLOCKCLSTATE();
3263				dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3264				    nfhp->nfh_len);
3265				if (dp != NULL) {
3266					if (NFSISSET_ATTRBIT(&attrbits,
3267					    NFSATTRBIT_SIZE)) {
3268						if (vp != NULL)
3269							va.va_size = np->n_size;
3270						else
3271							va.va_size =
3272							    dp->nfsdl_size;
3273						NFSSETBIT_ATTRBIT(&rattrbits,
3274						    NFSATTRBIT_SIZE);
3275					}
3276					if (NFSISSET_ATTRBIT(&attrbits,
3277					    NFSATTRBIT_CHANGE)) {
3278						va.va_filerev =
3279						    dp->nfsdl_change;
3280						if (vp == NULL ||
3281						    (np->n_flag & NDELEGMOD))
3282							va.va_filerev++;
3283						NFSSETBIT_ATTRBIT(&rattrbits,
3284						    NFSATTRBIT_CHANGE);
3285					}
3286				} else
3287					error = NFSERR_SERVERFAULT;
3288				NFSUNLOCKCLSTATE();
3289			}
3290			if (vp != NULL)
3291				vrele(vp);
3292			if (mp != NULL)
3293				vfs_unbusy(mp);
3294			if (nfhp != NULL)
3295				FREE((caddr_t)nfhp, M_NFSFH);
3296			if (!error)
3297				(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
3298				    NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0,
3299				    (uint64_t)0);
3300			break;
3301		case NFSV4OP_CBRECALL:
3302			NFSCL_DEBUG(4, "cbrecall\n");
3303			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
3304			    NFSX_UNSIGNED);
3305			stateid.seqid = *tl++;
3306			NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
3307			    NFSX_STATEIDOTHER);
3308			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3309			trunc = fxdr_unsigned(int, *tl);
3310			error = nfsm_getfh(nd, &nfhp);
3311			if (error == 0 && i == 0 &&
3312			    minorvers != NFSV4_MINORVERSION)
3313				error = NFSERR_OPNOTINSESS;
3314			if (!error) {
3315				NFSLOCKCLSTATE();
3316				if (minorvers == NFSV4_MINORVERSION)
3317					clp = nfscl_getclnt(cbident);
3318				else
3319					clp = nfscl_getclntsess(sessionid);
3320				if (clp != NULL) {
3321					dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3322					    nfhp->nfh_len);
3323					if (dp != NULL && (dp->nfsdl_flags &
3324					    NFSCLDL_DELEGRET) == 0) {
3325						dp->nfsdl_flags |=
3326						    NFSCLDL_RECALL;
3327						wakeup((caddr_t)clp);
3328					}
3329				} else {
3330					error = NFSERR_SERVERFAULT;
3331				}
3332				NFSUNLOCKCLSTATE();
3333			}
3334			if (nfhp != NULL)
3335				FREE((caddr_t)nfhp, M_NFSFH);
3336			break;
3337		case NFSV4OP_CBLAYOUTRECALL:
3338			NFSCL_DEBUG(4, "cblayrec\n");
3339			nfhp = NULL;
3340			NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
3341			laytype = fxdr_unsigned(int, *tl++);
3342			iomode = fxdr_unsigned(uint32_t, *tl++);
3343			if (newnfs_true == *tl++)
3344				changed = 1;
3345			else
3346				changed = 0;
3347			recalltype = fxdr_unsigned(int, *tl);
3348			recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL,
3349			    M_WAITOK);
3350			if (laytype != NFSLAYOUT_NFSV4_1_FILES)
3351				error = NFSERR_NOMATCHLAYOUT;
3352			else if (recalltype == NFSLAYOUTRETURN_FILE) {
3353				error = nfsm_getfh(nd, &nfhp);
3354				NFSCL_DEBUG(4, "retfile getfh=%d\n", error);
3355				if (error != 0)
3356					goto nfsmout;
3357				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER +
3358				    NFSX_STATEID);
3359				off = fxdr_hyper(tl); tl += 2;
3360				len = fxdr_hyper(tl); tl += 2;
3361				stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
3362				NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
3363				if (minorvers == NFSV4_MINORVERSION)
3364					error = NFSERR_NOTSUPP;
3365				else if (i == 0)
3366					error = NFSERR_OPNOTINSESS;
3367				if (error == 0) {
3368					NFSLOCKCLSTATE();
3369					clp = nfscl_getclntsess(sessionid);
3370					NFSCL_DEBUG(4, "cbly clp=%p\n", clp);
3371					if (clp != NULL) {
3372						lyp = nfscl_findlayout(clp,
3373						    nfhp->nfh_fh,
3374						    nfhp->nfh_len);
3375						NFSCL_DEBUG(4, "cblyp=%p\n",
3376						    lyp);
3377						if (lyp != NULL &&
3378						    (lyp->nfsly_flags &
3379						     NFSLY_FILES) != 0 &&
3380						    !NFSBCMP(stateid.other,
3381						    lyp->nfsly_stateid.other,
3382						    NFSX_STATEIDOTHER)) {
3383							error =
3384							    nfscl_layoutrecall(
3385							    recalltype,
3386							    lyp, iomode, off,
3387							    len, stateid.seqid,
3388							    recallp);
3389							recallp = NULL;
3390							wakeup(clp);
3391							NFSCL_DEBUG(4,
3392							    "aft layrcal=%d\n",
3393							    error);
3394						} else
3395							error =
3396							  NFSERR_NOMATCHLAYOUT;
3397					} else
3398						error = NFSERR_NOMATCHLAYOUT;
3399					NFSUNLOCKCLSTATE();
3400				}
3401				free(nfhp, M_NFSFH);
3402			} else if (recalltype == NFSLAYOUTRETURN_FSID) {
3403				NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER);
3404				filesid[0] = fxdr_hyper(tl); tl += 2;
3405				filesid[1] = fxdr_hyper(tl); tl += 2;
3406				gotone = 0;
3407				NFSLOCKCLSTATE();
3408				clp = nfscl_getclntsess(sessionid);
3409				if (clp != NULL) {
3410					TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3411					    nfsly_list) {
3412						if (lyp->nfsly_filesid[0] ==
3413						    filesid[0] &&
3414						    lyp->nfsly_filesid[1] ==
3415						    filesid[1]) {
3416							error =
3417							    nfscl_layoutrecall(
3418							    recalltype,
3419							    lyp, iomode, 0,
3420							    UINT64_MAX,
3421							    lyp->nfsly_stateid.seqid,
3422							    recallp);
3423							recallp = NULL;
3424							gotone = 1;
3425						}
3426					}
3427					if (gotone != 0)
3428						wakeup(clp);
3429					else
3430						error = NFSERR_NOMATCHLAYOUT;
3431				} else
3432					error = NFSERR_NOMATCHLAYOUT;
3433				NFSUNLOCKCLSTATE();
3434			} else if (recalltype == NFSLAYOUTRETURN_ALL) {
3435				gotone = 0;
3436				NFSLOCKCLSTATE();
3437				clp = nfscl_getclntsess(sessionid);
3438				if (clp != NULL) {
3439					TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3440					    nfsly_list) {
3441						error = nfscl_layoutrecall(
3442						    recalltype, lyp, iomode, 0,
3443						    UINT64_MAX,
3444						    lyp->nfsly_stateid.seqid,
3445						    recallp);
3446						recallp = NULL;
3447						gotone = 1;
3448					}
3449					if (gotone != 0)
3450						wakeup(clp);
3451					else
3452						error = NFSERR_NOMATCHLAYOUT;
3453				} else
3454					error = NFSERR_NOMATCHLAYOUT;
3455				NFSUNLOCKCLSTATE();
3456			} else
3457				error = NFSERR_NOMATCHLAYOUT;
3458			if (recallp != NULL) {
3459				free(recallp, M_NFSLAYRECALL);
3460				recallp = NULL;
3461			}
3462			break;
3463		case NFSV4OP_CBSEQUENCE:
3464			NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3465			    5 * NFSX_UNSIGNED);
3466			bcopy(tl, sessionid, NFSX_V4SESSIONID);
3467			tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3468			seqid = fxdr_unsigned(uint32_t, *tl++);
3469			slotid = fxdr_unsigned(uint32_t, *tl++);
3470			highslot = fxdr_unsigned(uint32_t, *tl++);
3471			cachethis = *tl++;
3472			/* Throw away the referring call stuff. */
3473			clist = fxdr_unsigned(int, *tl);
3474			for (j = 0; j < clist; j++) {
3475				NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3476				    NFSX_UNSIGNED);
3477				tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3478				rcalls = fxdr_unsigned(int, *tl);
3479				for (k = 0; k < rcalls; k++) {
3480					NFSM_DISSECT(tl, uint32_t *,
3481					    2 * NFSX_UNSIGNED);
3482				}
3483			}
3484			NFSLOCKCLSTATE();
3485			if (i == 0) {
3486				clp = nfscl_getclntsess(sessionid);
3487				if (clp == NULL)
3488					error = NFSERR_SERVERFAULT;
3489			} else
3490				error = NFSERR_SEQUENCEPOS;
3491			if (error == 0) {
3492				tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3493				error = nfsv4_seqsession(seqid, slotid,
3494				    highslot, tsep->nfsess_cbslots, &rep,
3495				    tsep->nfsess_backslots);
3496			}
3497			NFSUNLOCKCLSTATE();
3498			if (error == 0) {
3499				gotseq_ok = 1;
3500				if (rep != NULL) {
3501					NFSCL_DEBUG(4, "Got cbretry\n");
3502					m_freem(nd->nd_mreq);
3503					nd->nd_mreq = rep;
3504					rep = NULL;
3505					goto out;
3506				}
3507				NFSM_BUILD(tl, uint32_t *,
3508				    NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
3509				bcopy(sessionid, tl, NFSX_V4SESSIONID);
3510				tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3511				*tl++ = txdr_unsigned(seqid);
3512				*tl++ = txdr_unsigned(slotid);
3513				*tl++ = txdr_unsigned(NFSV4_CBSLOTS - 1);
3514				*tl = txdr_unsigned(NFSV4_CBSLOTS - 1);
3515			}
3516			break;
3517		default:
3518			if (i == 0 && minorvers == NFSV41_MINORVERSION)
3519				error = NFSERR_OPNOTINSESS;
3520			else {
3521				NFSCL_DEBUG(1, "unsupp callback %d\n", op);
3522				error = NFSERR_NOTSUPP;
3523			}
3524			break;
3525		};
3526		if (error) {
3527			if (error == EBADRPC || error == NFSERR_BADXDR) {
3528				nd->nd_repstat = NFSERR_BADXDR;
3529			} else {
3530				nd->nd_repstat = error;
3531			}
3532			error = 0;
3533		}
3534		retops++;
3535		if (nd->nd_repstat) {
3536			*repp = nfscl_errmap(nd, minorvers);
3537			break;
3538		} else
3539			*repp = 0;	/* NFS4_OK */
3540	}
3541nfsmout:
3542	if (recallp != NULL)
3543		free(recallp, M_NFSLAYRECALL);
3544	if (error) {
3545		if (error == EBADRPC || error == NFSERR_BADXDR)
3546			nd->nd_repstat = NFSERR_BADXDR;
3547		else
3548			printf("nfsv4 comperr1=%d\n", error);
3549	}
3550	if (taglen == -1) {
3551		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3552		*tl++ = 0;
3553		*tl = 0;
3554	} else {
3555		*retopsp = txdr_unsigned(retops);
3556	}
3557	*nd->nd_errp = nfscl_errmap(nd, minorvers);
3558out:
3559	if (gotseq_ok != 0) {
3560		rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK);
3561		NFSLOCKCLSTATE();
3562		clp = nfscl_getclntsess(sessionid);
3563		if (clp != NULL) {
3564			tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3565			nfsv4_seqsess_cacherep(slotid, tsep->nfsess_cbslots,
3566			    NFSERR_OK, &rep);
3567			NFSUNLOCKCLSTATE();
3568		} else {
3569			NFSUNLOCKCLSTATE();
3570			m_freem(rep);
3571		}
3572	}
3573}
3574
3575/*
3576 * Generate the next cbident value. Basically just increment a static value
3577 * and then check that it isn't already in the list, if it has wrapped around.
3578 */
3579static u_int32_t
3580nfscl_nextcbident(void)
3581{
3582	struct nfsclclient *clp;
3583	int matched;
3584	static u_int32_t nextcbident = 0;
3585	static int haswrapped = 0;
3586
3587	nextcbident++;
3588	if (nextcbident == 0)
3589		haswrapped = 1;
3590	if (haswrapped) {
3591		/*
3592		 * Search the clientid list for one already using this cbident.
3593		 */
3594		do {
3595			matched = 0;
3596			NFSLOCKCLSTATE();
3597			LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3598				if (clp->nfsc_cbident == nextcbident) {
3599					matched = 1;
3600					break;
3601				}
3602			}
3603			NFSUNLOCKCLSTATE();
3604			if (matched == 1)
3605				nextcbident++;
3606		} while (matched);
3607	}
3608	return (nextcbident);
3609}
3610
3611/*
3612 * Get the mount point related to a given cbident or session and busy it.
3613 */
3614static mount_t
3615nfscl_getmnt(int minorvers, uint8_t *sessionid, u_int32_t cbident,
3616    struct nfsclclient **clpp)
3617{
3618	struct nfsclclient *clp;
3619	mount_t mp;
3620	int error;
3621	struct nfsclsession *tsep;
3622
3623	*clpp = NULL;
3624	NFSLOCKCLSTATE();
3625	LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3626		tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3627		if (minorvers == NFSV4_MINORVERSION) {
3628			if (clp->nfsc_cbident == cbident)
3629				break;
3630		} else if (!NFSBCMP(tsep->nfsess_sessionid, sessionid,
3631		    NFSX_V4SESSIONID))
3632			break;
3633	}
3634	if (clp == NULL) {
3635		NFSUNLOCKCLSTATE();
3636		return (NULL);
3637	}
3638	mp = clp->nfsc_nmp->nm_mountp;
3639	vfs_ref(mp);
3640	NFSUNLOCKCLSTATE();
3641	error = vfs_busy(mp, 0);
3642	vfs_rel(mp);
3643	if (error != 0)
3644		return (NULL);
3645	*clpp = clp;
3646	return (mp);
3647}
3648
3649/*
3650 * Get the clientid pointer related to a given cbident.
3651 */
3652static struct nfsclclient *
3653nfscl_getclnt(u_int32_t cbident)
3654{
3655	struct nfsclclient *clp;
3656
3657	LIST_FOREACH(clp, &nfsclhead, nfsc_list)
3658		if (clp->nfsc_cbident == cbident)
3659			break;
3660	return (clp);
3661}
3662
3663/*
3664 * Get the clientid pointer related to a given sessionid.
3665 */
3666static struct nfsclclient *
3667nfscl_getclntsess(uint8_t *sessionid)
3668{
3669	struct nfsclclient *clp;
3670	struct nfsclsession *tsep;
3671
3672	LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3673		tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3674		if (!NFSBCMP(tsep->nfsess_sessionid, sessionid,
3675		    NFSX_V4SESSIONID))
3676			break;
3677	}
3678	return (clp);
3679}
3680
3681/*
3682 * Search for a lock conflict locally on the client. A conflict occurs if
3683 * - not same owner and overlapping byte range and at least one of them is
3684 *   a write lock or this is an unlock.
3685 */
3686static int
3687nfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
3688    struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
3689    struct nfscllock **lopp)
3690{
3691	struct nfsclowner *owp;
3692	struct nfsclopen *op;
3693	int ret;
3694
3695	if (dp != NULL) {
3696		ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
3697		if (ret)
3698			return (ret);
3699	}
3700	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3701		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3702			if (op->nfso_fhlen == fhlen &&
3703			    !NFSBCMP(op->nfso_fh, fhp, fhlen)) {
3704				ret = nfscl_checkconflict(&op->nfso_lock, nlop,
3705				    own, lopp);
3706				if (ret)
3707					return (ret);
3708			}
3709		}
3710	}
3711	return (0);
3712}
3713
3714static int
3715nfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
3716    u_int8_t *own, struct nfscllock **lopp)
3717{
3718	struct nfscllockowner *lp;
3719	struct nfscllock *lop;
3720
3721	LIST_FOREACH(lp, lhp, nfsl_list) {
3722		if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
3723			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3724				if (lop->nfslo_first >= nlop->nfslo_end)
3725					break;
3726				if (lop->nfslo_end <= nlop->nfslo_first)
3727					continue;
3728				if (lop->nfslo_type == F_WRLCK ||
3729				    nlop->nfslo_type == F_WRLCK ||
3730				    nlop->nfslo_type == F_UNLCK) {
3731					if (lopp != NULL)
3732						*lopp = lop;
3733					return (NFSERR_DENIED);
3734				}
3735			}
3736		}
3737	}
3738	return (0);
3739}
3740
3741/*
3742 * Check for a local conflicting lock.
3743 */
3744APPLESTATIC int
3745nfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
3746    u_int64_t len, struct flock *fl, NFSPROC_T *p, void *id, int flags)
3747{
3748	struct nfscllock *lop, nlck;
3749	struct nfscldeleg *dp;
3750	struct nfsnode *np;
3751	u_int8_t own[NFSV4CL_LOCKNAMELEN];
3752	int error;
3753
3754	nlck.nfslo_type = fl->l_type;
3755	nlck.nfslo_first = off;
3756	if (len == NFS64BITSSET) {
3757		nlck.nfslo_end = NFS64BITSSET;
3758	} else {
3759		nlck.nfslo_end = off + len;
3760		if (nlck.nfslo_end <= nlck.nfslo_first)
3761			return (NFSERR_INVAL);
3762	}
3763	np = VTONFS(vp);
3764	nfscl_filllockowner(id, own, flags);
3765	NFSLOCKCLSTATE();
3766	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3767	error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
3768	    &nlck, own, dp, &lop);
3769	if (error != 0) {
3770		fl->l_whence = SEEK_SET;
3771		fl->l_start = lop->nfslo_first;
3772		if (lop->nfslo_end == NFS64BITSSET)
3773			fl->l_len = 0;
3774		else
3775			fl->l_len = lop->nfslo_end - lop->nfslo_first;
3776		fl->l_pid = (pid_t)0;
3777		fl->l_type = lop->nfslo_type;
3778		error = -1;			/* no RPC required */
3779	} else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
3780	    fl->l_type == F_RDLCK)) {
3781		/*
3782		 * The delegation ensures that there isn't a conflicting
3783		 * lock on the server, so return -1 to indicate an RPC
3784		 * isn't required.
3785		 */
3786		fl->l_type = F_UNLCK;
3787		error = -1;
3788	}
3789	NFSUNLOCKCLSTATE();
3790	return (error);
3791}
3792
3793/*
3794 * Handle Recall of a delegation.
3795 * The clp must be exclusive locked when this is called.
3796 */
3797static int
3798nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
3799    struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p,
3800    int called_from_renewthread)
3801{
3802	struct nfsclowner *owp, *lowp, *nowp;
3803	struct nfsclopen *op, *lop;
3804	struct nfscllockowner *lp;
3805	struct nfscllock *lckp;
3806	struct nfsnode *np;
3807	int error = 0, ret, gotvp = 0;
3808
3809	if (vp == NULL) {
3810		/*
3811		 * First, get a vnode for the file. This is needed to do RPCs.
3812		 */
3813		ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
3814		    dp->nfsdl_fhlen, p, &np);
3815		if (ret) {
3816			/*
3817			 * File isn't open, so nothing to move over to the
3818			 * server.
3819			 */
3820			return (0);
3821		}
3822		vp = NFSTOV(np);
3823		gotvp = 1;
3824	} else {
3825		np = VTONFS(vp);
3826	}
3827	dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
3828
3829	/*
3830	 * Ok, if it's a write delegation, flush data to the server, so
3831	 * that close/open consistency is retained.
3832	 */
3833	ret = 0;
3834	NFSLOCKNODE(np);
3835	if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
3836		np->n_flag |= NDELEGRECALL;
3837		NFSUNLOCKNODE(np);
3838		ret = ncl_flush(vp, MNT_WAIT, cred, p, 1,
3839		    called_from_renewthread);
3840		NFSLOCKNODE(np);
3841		np->n_flag &= ~NDELEGRECALL;
3842	}
3843	NFSINVALATTRCACHE(np);
3844	NFSUNLOCKNODE(np);
3845	if (ret == EIO && called_from_renewthread != 0) {
3846		/*
3847		 * If the flush failed with EIO for the renew thread,
3848		 * return now, so that the dirty buffer will be flushed
3849		 * later.
3850		 */
3851		if (gotvp != 0)
3852			vrele(vp);
3853		return (ret);
3854	}
3855
3856	/*
3857	 * Now, for each openowner with opens issued locally, move them
3858	 * over to state against the server.
3859	 */
3860	LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
3861		lop = LIST_FIRST(&lowp->nfsow_open);
3862		if (lop != NULL) {
3863			if (LIST_NEXT(lop, nfso_list) != NULL)
3864				panic("nfsdlg mult opens");
3865			/*
3866			 * Look for the same openowner against the server.
3867			 */
3868			LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3869				if (!NFSBCMP(lowp->nfsow_owner,
3870				    owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
3871					newnfs_copycred(&dp->nfsdl_cred, cred);
3872					ret = nfscl_moveopen(vp, clp, nmp, lop,
3873					    owp, dp, cred, p);
3874					if (ret == NFSERR_STALECLIENTID ||
3875					    ret == NFSERR_STALEDONTRECOVER ||
3876					    ret == NFSERR_BADSESSION) {
3877						if (gotvp)
3878							vrele(vp);
3879						return (ret);
3880					}
3881					if (ret) {
3882						nfscl_freeopen(lop, 1);
3883						if (!error)
3884							error = ret;
3885					}
3886					break;
3887				}
3888			}
3889
3890			/*
3891			 * If no openowner found, create one and get an open
3892			 * for it.
3893			 */
3894			if (owp == NULL) {
3895				MALLOC(nowp, struct nfsclowner *,
3896				    sizeof (struct nfsclowner), M_NFSCLOWNER,
3897				    M_WAITOK);
3898				nfscl_newopen(clp, NULL, &owp, &nowp, &op,
3899				    NULL, lowp->nfsow_owner, dp->nfsdl_fh,
3900				    dp->nfsdl_fhlen, NULL);
3901				newnfs_copycred(&dp->nfsdl_cred, cred);
3902				ret = nfscl_moveopen(vp, clp, nmp, lop,
3903				    owp, dp, cred, p);
3904				if (ret) {
3905					nfscl_freeopenowner(owp, 0);
3906					if (ret == NFSERR_STALECLIENTID ||
3907					    ret == NFSERR_STALEDONTRECOVER ||
3908					    ret == NFSERR_BADSESSION) {
3909						if (gotvp)
3910							vrele(vp);
3911						return (ret);
3912					}
3913					if (ret) {
3914						nfscl_freeopen(lop, 1);
3915						if (!error)
3916							error = ret;
3917					}
3918				}
3919			}
3920		}
3921	}
3922
3923	/*
3924	 * Now, get byte range locks for any locks done locally.
3925	 */
3926	LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3927		LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
3928			newnfs_copycred(&dp->nfsdl_cred, cred);
3929			ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
3930			if (ret == NFSERR_STALESTATEID ||
3931			    ret == NFSERR_STALEDONTRECOVER ||
3932			    ret == NFSERR_STALECLIENTID ||
3933			    ret == NFSERR_BADSESSION) {
3934				if (gotvp)
3935					vrele(vp);
3936				return (ret);
3937			}
3938			if (ret && !error)
3939				error = ret;
3940		}
3941	}
3942	if (gotvp)
3943		vrele(vp);
3944	return (error);
3945}
3946
3947/*
3948 * Move a locally issued open over to an owner on the state list.
3949 * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
3950 * returns with it unlocked.
3951 */
3952static int
3953nfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3954    struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
3955    struct ucred *cred, NFSPROC_T *p)
3956{
3957	struct nfsclopen *op, *nop;
3958	struct nfscldeleg *ndp;
3959	struct nfsnode *np;
3960	int error = 0, newone;
3961
3962	/*
3963	 * First, look for an appropriate open, If found, just increment the
3964	 * opencnt in it.
3965	 */
3966	LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3967		if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
3968		    op->nfso_fhlen == lop->nfso_fhlen &&
3969		    !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
3970			op->nfso_opencnt += lop->nfso_opencnt;
3971			nfscl_freeopen(lop, 1);
3972			return (0);
3973		}
3974	}
3975
3976	/* No appropriate open, so we have to do one against the server. */
3977	np = VTONFS(vp);
3978	MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
3979	    lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
3980	newone = 0;
3981	nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
3982	    lop->nfso_fh, lop->nfso_fhlen, &newone);
3983	ndp = dp;
3984	error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen,
3985	    lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
3986	    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
3987	if (error) {
3988		if (newone)
3989			nfscl_freeopen(op, 0);
3990	} else {
3991		if (newone)
3992			newnfs_copyincred(cred, &op->nfso_cred);
3993		op->nfso_mode |= lop->nfso_mode;
3994		op->nfso_opencnt += lop->nfso_opencnt;
3995		nfscl_freeopen(lop, 1);
3996	}
3997	if (nop != NULL)
3998		FREE((caddr_t)nop, M_NFSCLOPEN);
3999	if (ndp != NULL) {
4000		/*
4001		 * What should I do with the returned delegation, since the
4002		 * delegation is being recalled? For now, just printf and
4003		 * through it away.
4004		 */
4005		printf("Moveopen returned deleg\n");
4006		FREE((caddr_t)ndp, M_NFSCLDELEG);
4007	}
4008	return (error);
4009}
4010
4011/*
4012 * Recall all delegations on this client.
4013 */
4014static void
4015nfscl_totalrecall(struct nfsclclient *clp)
4016{
4017	struct nfscldeleg *dp;
4018
4019	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
4020		if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0)
4021			dp->nfsdl_flags |= NFSCLDL_RECALL;
4022	}
4023}
4024
4025/*
4026 * Relock byte ranges. Called for delegation recall and state expiry.
4027 */
4028static int
4029nfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
4030    struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
4031    NFSPROC_T *p)
4032{
4033	struct nfscllockowner *nlp;
4034	struct nfsfh *nfhp;
4035	u_int64_t off, len;
4036	u_int32_t clidrev = 0;
4037	int error, newone, donelocally;
4038
4039	off = lop->nfslo_first;
4040	len = lop->nfslo_end - lop->nfslo_first;
4041	error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
4042	    clp, 1, NULL, lp->nfsl_lockflags, lp->nfsl_owner,
4043	    lp->nfsl_openowner, &nlp, &newone, &donelocally);
4044	if (error || donelocally)
4045		return (error);
4046	if (nmp->nm_clp != NULL)
4047		clidrev = nmp->nm_clp->nfsc_clientidrev;
4048	else
4049		clidrev = 0;
4050	nfhp = VTONFS(vp)->n_fhp;
4051	error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
4052	    nfhp->nfh_len, nlp, newone, 0, off,
4053	    len, lop->nfslo_type, cred, p);
4054	if (error)
4055		nfscl_freelockowner(nlp, 0);
4056	return (error);
4057}
4058
4059/*
4060 * Called to re-open a file. Basically get a vnode for the file handle
4061 * and then call nfsrpc_openrpc() to do the rest.
4062 */
4063static int
4064nfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
4065    u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
4066    struct ucred *cred, NFSPROC_T *p)
4067{
4068	struct nfsnode *np;
4069	vnode_t vp;
4070	int error;
4071
4072	error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
4073	if (error)
4074		return (error);
4075	vp = NFSTOV(np);
4076	if (np->n_v4 != NULL) {
4077		error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
4078		    np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
4079		    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
4080		    cred, p);
4081	} else {
4082		error = EINVAL;
4083	}
4084	vrele(vp);
4085	return (error);
4086}
4087
4088/*
4089 * Try an open against the server. Just call nfsrpc_openrpc(), retrying while
4090 * NFSERR_DELAY. Also, try system credentials, if the passed in credentials
4091 * fail.
4092 */
4093static int
4094nfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4095    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
4096    u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
4097    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
4098{
4099	int error;
4100
4101	do {
4102		error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
4103		    mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
4104		    0, 0);
4105		if (error == NFSERR_DELAY)
4106			(void) nfs_catnap(PZERO, error, "nfstryop");
4107	} while (error == NFSERR_DELAY);
4108	if (error == EAUTH || error == EACCES) {
4109		/* Try again using system credentials */
4110		newnfs_setroot(cred);
4111		do {
4112		    error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
4113			newfhlen, mode, op, name, namelen, ndpp, reclaim,
4114			delegtype, cred, p, 1, 0);
4115		    if (error == NFSERR_DELAY)
4116			(void) nfs_catnap(PZERO, error, "nfstryop");
4117		} while (error == NFSERR_DELAY);
4118	}
4119	return (error);
4120}
4121
4122/*
4123 * Try a byte range lock. Just loop on nfsrpc_lock() while it returns
4124 * NFSERR_DELAY. Also, retry with system credentials, if the provided
4125 * cred don't work.
4126 */
4127static int
4128nfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
4129    int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
4130    u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
4131{
4132	struct nfsrv_descript nfsd, *nd = &nfsd;
4133	int error;
4134
4135	do {
4136		error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
4137		    reclaim, off, len, type, cred, p, 0);
4138		if (!error && nd->nd_repstat == NFSERR_DELAY)
4139			(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4140			    "nfstrylck");
4141	} while (!error && nd->nd_repstat == NFSERR_DELAY);
4142	if (!error)
4143		error = nd->nd_repstat;
4144	if (error == EAUTH || error == EACCES) {
4145		/* Try again using root credentials */
4146		newnfs_setroot(cred);
4147		do {
4148			error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
4149			    newone, reclaim, off, len, type, cred, p, 1);
4150			if (!error && nd->nd_repstat == NFSERR_DELAY)
4151				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4152				    "nfstrylck");
4153		} while (!error && nd->nd_repstat == NFSERR_DELAY);
4154		if (!error)
4155			error = nd->nd_repstat;
4156	}
4157	return (error);
4158}
4159
4160/*
4161 * Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
4162 * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4163 * credentials fail.
4164 */
4165static int
4166nfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
4167    struct nfsmount *nmp, NFSPROC_T *p)
4168{
4169	int error;
4170
4171	do {
4172		error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
4173		if (error == NFSERR_DELAY)
4174			(void) nfs_catnap(PZERO, error, "nfstrydp");
4175	} while (error == NFSERR_DELAY);
4176	if (error == EAUTH || error == EACCES) {
4177		/* Try again using system credentials */
4178		newnfs_setroot(cred);
4179		do {
4180			error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
4181			if (error == NFSERR_DELAY)
4182				(void) nfs_catnap(PZERO, error, "nfstrydp");
4183		} while (error == NFSERR_DELAY);
4184	}
4185	return (error);
4186}
4187
4188/*
4189 * Try a close against the server. Just call nfsrpc_closerpc(),
4190 * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4191 * credentials fail.
4192 */
4193APPLESTATIC int
4194nfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
4195    struct nfsmount *nmp, NFSPROC_T *p)
4196{
4197	struct nfsrv_descript nfsd, *nd = &nfsd;
4198	int error;
4199
4200	do {
4201		error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
4202		if (error == NFSERR_DELAY)
4203			(void) nfs_catnap(PZERO, error, "nfstrycl");
4204	} while (error == NFSERR_DELAY);
4205	if (error == EAUTH || error == EACCES) {
4206		/* Try again using system credentials */
4207		newnfs_setroot(cred);
4208		do {
4209			error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
4210			if (error == NFSERR_DELAY)
4211				(void) nfs_catnap(PZERO, error, "nfstrycl");
4212		} while (error == NFSERR_DELAY);
4213	}
4214	return (error);
4215}
4216
4217/*
4218 * Decide if a delegation on a file permits close without flushing writes
4219 * to the server. This might be a big performance win in some environments.
4220 * (Not useful until the client does caching on local stable storage.)
4221 */
4222APPLESTATIC int
4223nfscl_mustflush(vnode_t vp)
4224{
4225	struct nfsclclient *clp;
4226	struct nfscldeleg *dp;
4227	struct nfsnode *np;
4228	struct nfsmount *nmp;
4229
4230	np = VTONFS(vp);
4231	nmp = VFSTONFS(vnode_mount(vp));
4232	if (!NFSHASNFSV4(nmp))
4233		return (1);
4234	NFSLOCKCLSTATE();
4235	clp = nfscl_findcl(nmp);
4236	if (clp == NULL) {
4237		NFSUNLOCKCLSTATE();
4238		return (1);
4239	}
4240	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4241	if (dp != NULL && (dp->nfsdl_flags &
4242	    (NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) ==
4243	     NFSCLDL_WRITE &&
4244	    (dp->nfsdl_sizelimit >= np->n_size ||
4245	     !NFSHASSTRICT3530(nmp))) {
4246		NFSUNLOCKCLSTATE();
4247		return (0);
4248	}
4249	NFSUNLOCKCLSTATE();
4250	return (1);
4251}
4252
4253/*
4254 * See if a (write) delegation exists for this file.
4255 */
4256APPLESTATIC int
4257nfscl_nodeleg(vnode_t vp, int writedeleg)
4258{
4259	struct nfsclclient *clp;
4260	struct nfscldeleg *dp;
4261	struct nfsnode *np;
4262	struct nfsmount *nmp;
4263
4264	np = VTONFS(vp);
4265	nmp = VFSTONFS(vnode_mount(vp));
4266	if (!NFSHASNFSV4(nmp))
4267		return (1);
4268	NFSLOCKCLSTATE();
4269	clp = nfscl_findcl(nmp);
4270	if (clp == NULL) {
4271		NFSUNLOCKCLSTATE();
4272		return (1);
4273	}
4274	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4275	if (dp != NULL &&
4276	    (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 &&
4277	    (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) ==
4278	     NFSCLDL_WRITE)) {
4279		NFSUNLOCKCLSTATE();
4280		return (0);
4281	}
4282	NFSUNLOCKCLSTATE();
4283	return (1);
4284}
4285
4286/*
4287 * Look for an associated delegation that should be DelegReturned.
4288 */
4289APPLESTATIC int
4290nfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
4291{
4292	struct nfsclclient *clp;
4293	struct nfscldeleg *dp;
4294	struct nfsclowner *owp;
4295	struct nfscllockowner *lp;
4296	struct nfsmount *nmp;
4297	struct ucred *cred;
4298	struct nfsnode *np;
4299	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4300
4301	nmp = VFSTONFS(vnode_mount(vp));
4302	np = VTONFS(vp);
4303	NFSLOCKCLSTATE();
4304	/*
4305	 * Loop around waiting for:
4306	 * - outstanding I/O operations on delegations to complete
4307	 * - for a delegation on vp that has state, lock the client and
4308	 *   do a recall
4309	 * - return delegation with no state
4310	 */
4311	while (1) {
4312		clp = nfscl_findcl(nmp);
4313		if (clp == NULL) {
4314			NFSUNLOCKCLSTATE();
4315			return (retcnt);
4316		}
4317		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4318		    np->n_fhp->nfh_len);
4319		if (dp != NULL) {
4320		    /*
4321		     * Wait for outstanding I/O ops to be done.
4322		     */
4323		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4324			if (igotlock) {
4325			    nfsv4_unlock(&clp->nfsc_lock, 0);
4326			    igotlock = 0;
4327			}
4328			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4329			(void) nfsmsleep(&dp->nfsdl_rwlock,
4330			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4331			continue;
4332		    }
4333		    needsrecall = 0;
4334		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4335			if (!LIST_EMPTY(&owp->nfsow_open)) {
4336			    needsrecall = 1;
4337			    break;
4338			}
4339		    }
4340		    if (!needsrecall) {
4341			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4342			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4343				needsrecall = 1;
4344				break;
4345			    }
4346			}
4347		    }
4348		    if (needsrecall && !triedrecall) {
4349			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4350			islept = 0;
4351			while (!igotlock) {
4352			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4353				&islept, NFSCLSTATEMUTEXPTR, NULL);
4354			    if (islept)
4355				break;
4356			}
4357			if (islept)
4358			    continue;
4359			NFSUNLOCKCLSTATE();
4360			cred = newnfs_getcred();
4361			newnfs_copycred(&dp->nfsdl_cred, cred);
4362			(void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0);
4363			NFSFREECRED(cred);
4364			triedrecall = 1;
4365			NFSLOCKCLSTATE();
4366			nfsv4_unlock(&clp->nfsc_lock, 0);
4367			igotlock = 0;
4368			continue;
4369		    }
4370		    *stp = dp->nfsdl_stateid;
4371		    retcnt = 1;
4372		    nfscl_cleandeleg(dp);
4373		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4374		}
4375		if (igotlock)
4376		    nfsv4_unlock(&clp->nfsc_lock, 0);
4377		NFSUNLOCKCLSTATE();
4378		return (retcnt);
4379	}
4380}
4381
4382/*
4383 * Look for associated delegation(s) that should be DelegReturned.
4384 */
4385APPLESTATIC int
4386nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
4387    nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
4388{
4389	struct nfsclclient *clp;
4390	struct nfscldeleg *dp;
4391	struct nfsclowner *owp;
4392	struct nfscllockowner *lp;
4393	struct nfsmount *nmp;
4394	struct ucred *cred;
4395	struct nfsnode *np;
4396	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4397
4398	nmp = VFSTONFS(vnode_mount(fvp));
4399	*gotfdp = 0;
4400	*gottdp = 0;
4401	NFSLOCKCLSTATE();
4402	/*
4403	 * Loop around waiting for:
4404	 * - outstanding I/O operations on delegations to complete
4405	 * - for a delegation on fvp that has state, lock the client and
4406	 *   do a recall
4407	 * - return delegation(s) with no state.
4408	 */
4409	while (1) {
4410		clp = nfscl_findcl(nmp);
4411		if (clp == NULL) {
4412			NFSUNLOCKCLSTATE();
4413			return (retcnt);
4414		}
4415		np = VTONFS(fvp);
4416		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4417		    np->n_fhp->nfh_len);
4418		if (dp != NULL && *gotfdp == 0) {
4419		    /*
4420		     * Wait for outstanding I/O ops to be done.
4421		     */
4422		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4423			if (igotlock) {
4424			    nfsv4_unlock(&clp->nfsc_lock, 0);
4425			    igotlock = 0;
4426			}
4427			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4428			(void) nfsmsleep(&dp->nfsdl_rwlock,
4429			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4430			continue;
4431		    }
4432		    needsrecall = 0;
4433		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4434			if (!LIST_EMPTY(&owp->nfsow_open)) {
4435			    needsrecall = 1;
4436			    break;
4437			}
4438		    }
4439		    if (!needsrecall) {
4440			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4441			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4442				needsrecall = 1;
4443				break;
4444			    }
4445			}
4446		    }
4447		    if (needsrecall && !triedrecall) {
4448			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4449			islept = 0;
4450			while (!igotlock) {
4451			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4452				&islept, NFSCLSTATEMUTEXPTR, NULL);
4453			    if (islept)
4454				break;
4455			}
4456			if (islept)
4457			    continue;
4458			NFSUNLOCKCLSTATE();
4459			cred = newnfs_getcred();
4460			newnfs_copycred(&dp->nfsdl_cred, cred);
4461			(void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0);
4462			NFSFREECRED(cred);
4463			triedrecall = 1;
4464			NFSLOCKCLSTATE();
4465			nfsv4_unlock(&clp->nfsc_lock, 0);
4466			igotlock = 0;
4467			continue;
4468		    }
4469		    *fstp = dp->nfsdl_stateid;
4470		    retcnt++;
4471		    *gotfdp = 1;
4472		    nfscl_cleandeleg(dp);
4473		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4474		}
4475		if (igotlock) {
4476		    nfsv4_unlock(&clp->nfsc_lock, 0);
4477		    igotlock = 0;
4478		}
4479		if (tvp != NULL) {
4480		    np = VTONFS(tvp);
4481		    dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4482			np->n_fhp->nfh_len);
4483		    if (dp != NULL && *gottdp == 0) {
4484			/*
4485			 * Wait for outstanding I/O ops to be done.
4486			 */
4487			if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4488			    dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4489			    (void) nfsmsleep(&dp->nfsdl_rwlock,
4490				NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4491			    continue;
4492			}
4493			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4494			    if (!LIST_EMPTY(&owp->nfsow_open)) {
4495				NFSUNLOCKCLSTATE();
4496				return (retcnt);
4497			    }
4498			}
4499			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4500			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4501				NFSUNLOCKCLSTATE();
4502				return (retcnt);
4503			    }
4504			}
4505			*tstp = dp->nfsdl_stateid;
4506			retcnt++;
4507			*gottdp = 1;
4508			nfscl_cleandeleg(dp);
4509			nfscl_freedeleg(&clp->nfsc_deleg, dp);
4510		    }
4511		}
4512		NFSUNLOCKCLSTATE();
4513		return (retcnt);
4514	}
4515}
4516
4517/*
4518 * Get a reference on the clientid associated with the mount point.
4519 * Return 1 if success, 0 otherwise.
4520 */
4521APPLESTATIC int
4522nfscl_getref(struct nfsmount *nmp)
4523{
4524	struct nfsclclient *clp;
4525
4526	NFSLOCKCLSTATE();
4527	clp = nfscl_findcl(nmp);
4528	if (clp == NULL) {
4529		NFSUNLOCKCLSTATE();
4530		return (0);
4531	}
4532	nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL);
4533	NFSUNLOCKCLSTATE();
4534	return (1);
4535}
4536
4537/*
4538 * Release a reference on a clientid acquired with the above call.
4539 */
4540APPLESTATIC void
4541nfscl_relref(struct nfsmount *nmp)
4542{
4543	struct nfsclclient *clp;
4544
4545	NFSLOCKCLSTATE();
4546	clp = nfscl_findcl(nmp);
4547	if (clp == NULL) {
4548		NFSUNLOCKCLSTATE();
4549		return;
4550	}
4551	nfsv4_relref(&clp->nfsc_lock);
4552	NFSUNLOCKCLSTATE();
4553}
4554
4555/*
4556 * Save the size attribute in the delegation, since the nfsnode
4557 * is going away.
4558 */
4559APPLESTATIC void
4560nfscl_reclaimnode(vnode_t vp)
4561{
4562	struct nfsclclient *clp;
4563	struct nfscldeleg *dp;
4564	struct nfsnode *np = VTONFS(vp);
4565	struct nfsmount *nmp;
4566
4567	nmp = VFSTONFS(vnode_mount(vp));
4568	if (!NFSHASNFSV4(nmp))
4569		return;
4570	NFSLOCKCLSTATE();
4571	clp = nfscl_findcl(nmp);
4572	if (clp == NULL) {
4573		NFSUNLOCKCLSTATE();
4574		return;
4575	}
4576	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4577	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4578		dp->nfsdl_size = np->n_size;
4579	NFSUNLOCKCLSTATE();
4580}
4581
4582/*
4583 * Get the saved size attribute in the delegation, since it is a
4584 * newly allocated nfsnode.
4585 */
4586APPLESTATIC void
4587nfscl_newnode(vnode_t vp)
4588{
4589	struct nfsclclient *clp;
4590	struct nfscldeleg *dp;
4591	struct nfsnode *np = VTONFS(vp);
4592	struct nfsmount *nmp;
4593
4594	nmp = VFSTONFS(vnode_mount(vp));
4595	if (!NFSHASNFSV4(nmp))
4596		return;
4597	NFSLOCKCLSTATE();
4598	clp = nfscl_findcl(nmp);
4599	if (clp == NULL) {
4600		NFSUNLOCKCLSTATE();
4601		return;
4602	}
4603	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4604	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4605		np->n_size = dp->nfsdl_size;
4606	NFSUNLOCKCLSTATE();
4607}
4608
4609/*
4610 * If there is a valid write delegation for this file, set the modtime
4611 * to the local clock time.
4612 */
4613APPLESTATIC void
4614nfscl_delegmodtime(vnode_t vp)
4615{
4616	struct nfsclclient *clp;
4617	struct nfscldeleg *dp;
4618	struct nfsnode *np = VTONFS(vp);
4619	struct nfsmount *nmp;
4620
4621	nmp = VFSTONFS(vnode_mount(vp));
4622	if (!NFSHASNFSV4(nmp))
4623		return;
4624	NFSLOCKCLSTATE();
4625	clp = nfscl_findcl(nmp);
4626	if (clp == NULL) {
4627		NFSUNLOCKCLSTATE();
4628		return;
4629	}
4630	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4631	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
4632		nanotime(&dp->nfsdl_modtime);
4633		dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
4634	}
4635	NFSUNLOCKCLSTATE();
4636}
4637
4638/*
4639 * If there is a valid write delegation for this file with a modtime set,
4640 * put that modtime in mtime.
4641 */
4642APPLESTATIC void
4643nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
4644{
4645	struct nfsclclient *clp;
4646	struct nfscldeleg *dp;
4647	struct nfsnode *np = VTONFS(vp);
4648	struct nfsmount *nmp;
4649
4650	nmp = VFSTONFS(vnode_mount(vp));
4651	if (!NFSHASNFSV4(nmp))
4652		return;
4653	NFSLOCKCLSTATE();
4654	clp = nfscl_findcl(nmp);
4655	if (clp == NULL) {
4656		NFSUNLOCKCLSTATE();
4657		return;
4658	}
4659	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4660	if (dp != NULL &&
4661	    (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
4662	    (NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
4663		*mtime = dp->nfsdl_modtime;
4664	NFSUNLOCKCLSTATE();
4665}
4666
4667static int
4668nfscl_errmap(struct nfsrv_descript *nd, u_int32_t minorvers)
4669{
4670	short *defaulterrp, *errp;
4671
4672	if (!nd->nd_repstat)
4673		return (0);
4674	if (nd->nd_procnum == NFSPROC_NOOP)
4675		return (txdr_unsigned(nd->nd_repstat & 0xffff));
4676	if (nd->nd_repstat == EBADRPC)
4677		return (txdr_unsigned(NFSERR_BADXDR));
4678	if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
4679	    nd->nd_repstat == NFSERR_OPILLEGAL)
4680		return (txdr_unsigned(nd->nd_repstat));
4681	if (nd->nd_repstat >= NFSERR_BADIOMODE && nd->nd_repstat < 20000 &&
4682	    minorvers > NFSV4_MINORVERSION) {
4683		/* NFSv4.n error. */
4684		return (txdr_unsigned(nd->nd_repstat));
4685	}
4686	if (nd->nd_procnum < NFSV4OP_CBNOPS)
4687		errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
4688	else
4689		return (txdr_unsigned(nd->nd_repstat));
4690	while (*++errp)
4691		if (*errp == (short)nd->nd_repstat)
4692			return (txdr_unsigned(nd->nd_repstat));
4693	return (txdr_unsigned(*defaulterrp));
4694}
4695
4696/*
4697 * Called to find/add a layout to a client.
4698 * This function returns the layout with a refcnt (shared lock) upon
4699 * success (returns 0) or with no lock/refcnt on the layout when an
4700 * error is returned.
4701 * If a layout is passed in via lypp, it is locked (exclusively locked).
4702 */
4703APPLESTATIC int
4704nfscl_layout(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4705    nfsv4stateid_t *stateidp, int retonclose,
4706    struct nfsclflayouthead *fhlp, struct nfscllayout **lypp,
4707    struct ucred *cred, NFSPROC_T *p)
4708{
4709	struct nfsclclient *clp;
4710	struct nfscllayout *lyp, *tlyp;
4711	struct nfsclflayout *flp;
4712	struct nfsnode *np = VTONFS(vp);
4713	mount_t mp;
4714	int layout_passed_in;
4715
4716	mp = nmp->nm_mountp;
4717	layout_passed_in = 1;
4718	tlyp = NULL;
4719	lyp = *lypp;
4720	if (lyp == NULL) {
4721		layout_passed_in = 0;
4722		tlyp = malloc(sizeof(*tlyp) + fhlen - 1, M_NFSLAYOUT,
4723		    M_WAITOK | M_ZERO);
4724	}
4725
4726	NFSLOCKCLSTATE();
4727	clp = nmp->nm_clp;
4728	if (clp == NULL) {
4729		if (layout_passed_in != 0)
4730			nfsv4_unlock(&lyp->nfsly_lock, 0);
4731		NFSUNLOCKCLSTATE();
4732		if (tlyp != NULL)
4733			free(tlyp, M_NFSLAYOUT);
4734		return (EPERM);
4735	}
4736	if (lyp == NULL) {
4737		/*
4738		 * Although no lyp was passed in, another thread might have
4739		 * allocated one. If one is found, just increment it's ref
4740		 * count and return it.
4741		 */
4742		lyp = nfscl_findlayout(clp, fhp, fhlen);
4743		if (lyp == NULL) {
4744			lyp = tlyp;
4745			tlyp = NULL;
4746			lyp->nfsly_stateid.seqid = stateidp->seqid;
4747			lyp->nfsly_stateid.other[0] = stateidp->other[0];
4748			lyp->nfsly_stateid.other[1] = stateidp->other[1];
4749			lyp->nfsly_stateid.other[2] = stateidp->other[2];
4750			lyp->nfsly_lastbyte = 0;
4751			LIST_INIT(&lyp->nfsly_flayread);
4752			LIST_INIT(&lyp->nfsly_flayrw);
4753			LIST_INIT(&lyp->nfsly_recall);
4754			lyp->nfsly_filesid[0] = np->n_vattr.na_filesid[0];
4755			lyp->nfsly_filesid[1] = np->n_vattr.na_filesid[1];
4756			lyp->nfsly_clp = clp;
4757			lyp->nfsly_flags = (retonclose != 0) ?
4758			    (NFSLY_FILES | NFSLY_RETONCLOSE) : NFSLY_FILES;
4759			lyp->nfsly_fhlen = fhlen;
4760			NFSBCOPY(fhp, lyp->nfsly_fh, fhlen);
4761			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4762			LIST_INSERT_HEAD(NFSCLLAYOUTHASH(clp, fhp, fhlen), lyp,
4763			    nfsly_hash);
4764			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4765			nfscl_layoutcnt++;
4766		} else {
4767			if (retonclose != 0)
4768				lyp->nfsly_flags |= NFSLY_RETONCLOSE;
4769			TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
4770			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4771			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4772		}
4773		nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
4774		if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
4775			NFSUNLOCKCLSTATE();
4776			if (tlyp != NULL)
4777				free(tlyp, M_NFSLAYOUT);
4778			return (EPERM);
4779		}
4780		*lypp = lyp;
4781	} else
4782		lyp->nfsly_stateid.seqid = stateidp->seqid;
4783
4784	/* Merge the new list of File Layouts into the list. */
4785	flp = LIST_FIRST(fhlp);
4786	if (flp != NULL) {
4787		if (flp->nfsfl_iomode == NFSLAYOUTIOMODE_READ)
4788			nfscl_mergeflayouts(&lyp->nfsly_flayread, fhlp);
4789		else
4790			nfscl_mergeflayouts(&lyp->nfsly_flayrw, fhlp);
4791	}
4792	if (layout_passed_in != 0)
4793		nfsv4_unlock(&lyp->nfsly_lock, 1);
4794	NFSUNLOCKCLSTATE();
4795	if (tlyp != NULL)
4796		free(tlyp, M_NFSLAYOUT);
4797	return (0);
4798}
4799
4800/*
4801 * Search for a layout by MDS file handle.
4802 * If one is found, it is returned with a refcnt (shared lock) iff
4803 * retflpp returned non-NULL and locked (exclusive locked) iff retflpp is
4804 * returned NULL.
4805 */
4806struct nfscllayout *
4807nfscl_getlayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen,
4808    uint64_t off, struct nfsclflayout **retflpp, int *recalledp)
4809{
4810	struct nfscllayout *lyp;
4811	mount_t mp;
4812	int error, igotlock;
4813
4814	mp = clp->nfsc_nmp->nm_mountp;
4815	*recalledp = 0;
4816	*retflpp = NULL;
4817	NFSLOCKCLSTATE();
4818	lyp = nfscl_findlayout(clp, fhp, fhlen);
4819	if (lyp != NULL) {
4820		if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) {
4821			TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
4822			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4823			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4824			error = nfscl_findlayoutforio(lyp, off,
4825			    NFSV4OPEN_ACCESSREAD, retflpp);
4826			if (error == 0)
4827				nfsv4_getref(&lyp->nfsly_lock, NULL,
4828				    NFSCLSTATEMUTEXPTR, mp);
4829			else {
4830				do {
4831					igotlock = nfsv4_lock(&lyp->nfsly_lock,
4832					    1, NULL, NFSCLSTATEMUTEXPTR, mp);
4833				} while (igotlock == 0 &&
4834				    (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0);
4835				*retflpp = NULL;
4836			}
4837			if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
4838				lyp = NULL;
4839				*recalledp = 1;
4840			}
4841		} else {
4842			lyp = NULL;
4843			*recalledp = 1;
4844		}
4845	}
4846	NFSUNLOCKCLSTATE();
4847	return (lyp);
4848}
4849
4850/*
4851 * Search for a layout by MDS file handle. If one is found that is marked
4852 * "return on close", delete it, since it should now be forgotten.
4853 */
4854static void
4855nfscl_retoncloselayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen)
4856{
4857	struct nfscllayout *lyp;
4858
4859tryagain:
4860	lyp = nfscl_findlayout(clp, fhp, fhlen);
4861	if (lyp != NULL && (lyp->nfsly_flags & NFSLY_RETONCLOSE) != 0) {
4862		/*
4863		 * Wait for outstanding I/O ops to be done.
4864		 */
4865		if (lyp->nfsly_lock.nfslock_usecnt != 0 ||
4866		    lyp->nfsly_lock.nfslock_lock != 0) {
4867			lyp->nfsly_lock.nfslock_lock |= NFSV4LOCK_WANTED;
4868			(void)mtx_sleep(&lyp->nfsly_lock,
4869			    NFSCLSTATEMUTEXPTR, PZERO, "nfslyc", 0);
4870			goto tryagain;
4871		}
4872		nfscl_freelayout(lyp);
4873	}
4874}
4875
4876/*
4877 * Dereference a layout.
4878 */
4879void
4880nfscl_rellayout(struct nfscllayout *lyp, int exclocked)
4881{
4882
4883	NFSLOCKCLSTATE();
4884	if (exclocked != 0)
4885		nfsv4_unlock(&lyp->nfsly_lock, 0);
4886	else
4887		nfsv4_relref(&lyp->nfsly_lock);
4888	NFSUNLOCKCLSTATE();
4889}
4890
4891/*
4892 * Search for a devinfo by deviceid. If one is found, return it after
4893 * acquiring a reference count on it.
4894 */
4895struct nfscldevinfo *
4896nfscl_getdevinfo(struct nfsclclient *clp, uint8_t *deviceid,
4897    struct nfscldevinfo *dip)
4898{
4899
4900	NFSLOCKCLSTATE();
4901	if (dip == NULL)
4902		dip = nfscl_finddevinfo(clp, deviceid);
4903	if (dip != NULL)
4904		dip->nfsdi_refcnt++;
4905	NFSUNLOCKCLSTATE();
4906	return (dip);
4907}
4908
4909/*
4910 * Dereference a devinfo structure.
4911 */
4912static void
4913nfscl_reldevinfo_locked(struct nfscldevinfo *dip)
4914{
4915
4916	dip->nfsdi_refcnt--;
4917	if (dip->nfsdi_refcnt == 0)
4918		wakeup(&dip->nfsdi_refcnt);
4919}
4920
4921/*
4922 * Dereference a devinfo structure.
4923 */
4924void
4925nfscl_reldevinfo(struct nfscldevinfo *dip)
4926{
4927
4928	NFSLOCKCLSTATE();
4929	nfscl_reldevinfo_locked(dip);
4930	NFSUNLOCKCLSTATE();
4931}
4932
4933/*
4934 * Find a layout for this file handle. Return NULL upon failure.
4935 */
4936static struct nfscllayout *
4937nfscl_findlayout(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
4938{
4939	struct nfscllayout *lyp;
4940
4941	LIST_FOREACH(lyp, NFSCLLAYOUTHASH(clp, fhp, fhlen), nfsly_hash)
4942		if (lyp->nfsly_fhlen == fhlen &&
4943		    !NFSBCMP(lyp->nfsly_fh, fhp, fhlen))
4944			break;
4945	return (lyp);
4946}
4947
4948/*
4949 * Find a devinfo for this deviceid. Return NULL upon failure.
4950 */
4951static struct nfscldevinfo *
4952nfscl_finddevinfo(struct nfsclclient *clp, uint8_t *deviceid)
4953{
4954	struct nfscldevinfo *dip;
4955
4956	LIST_FOREACH(dip, &clp->nfsc_devinfo, nfsdi_list)
4957		if (NFSBCMP(dip->nfsdi_deviceid, deviceid, NFSX_V4DEVICEID)
4958		    == 0)
4959			break;
4960	return (dip);
4961}
4962
4963/*
4964 * Merge the new file layout list into the main one, maintaining it in
4965 * increasing offset order.
4966 */
4967static void
4968nfscl_mergeflayouts(struct nfsclflayouthead *fhlp,
4969    struct nfsclflayouthead *newfhlp)
4970{
4971	struct nfsclflayout *flp, *nflp, *prevflp, *tflp;
4972
4973	flp = LIST_FIRST(fhlp);
4974	prevflp = NULL;
4975	LIST_FOREACH_SAFE(nflp, newfhlp, nfsfl_list, tflp) {
4976		while (flp != NULL && flp->nfsfl_off < nflp->nfsfl_off) {
4977			prevflp = flp;
4978			flp = LIST_NEXT(flp, nfsfl_list);
4979		}
4980		if (prevflp == NULL)
4981			LIST_INSERT_HEAD(fhlp, nflp, nfsfl_list);
4982		else
4983			LIST_INSERT_AFTER(prevflp, nflp, nfsfl_list);
4984		prevflp = nflp;
4985	}
4986}
4987
4988/*
4989 * Add this nfscldevinfo to the client, if it doesn't already exist.
4990 * This function consumes the structure pointed at by dip, if not NULL.
4991 */
4992APPLESTATIC int
4993nfscl_adddevinfo(struct nfsmount *nmp, struct nfscldevinfo *dip,
4994    struct nfsclflayout *flp)
4995{
4996	struct nfsclclient *clp;
4997	struct nfscldevinfo *tdip;
4998
4999	NFSLOCKCLSTATE();
5000	clp = nmp->nm_clp;
5001	if (clp == NULL) {
5002		NFSUNLOCKCLSTATE();
5003		if (dip != NULL)
5004			free(dip, M_NFSDEVINFO);
5005		return (ENODEV);
5006	}
5007	tdip = nfscl_finddevinfo(clp, flp->nfsfl_dev);
5008	if (tdip != NULL) {
5009		tdip->nfsdi_layoutrefs++;
5010		flp->nfsfl_devp = tdip;
5011		nfscl_reldevinfo_locked(tdip);
5012		NFSUNLOCKCLSTATE();
5013		if (dip != NULL)
5014			free(dip, M_NFSDEVINFO);
5015		return (0);
5016	}
5017	if (dip != NULL) {
5018		LIST_INSERT_HEAD(&clp->nfsc_devinfo, dip, nfsdi_list);
5019		dip->nfsdi_layoutrefs = 1;
5020		flp->nfsfl_devp = dip;
5021	}
5022	NFSUNLOCKCLSTATE();
5023	if (dip == NULL)
5024		return (ENODEV);
5025	return (0);
5026}
5027
5028/*
5029 * Free up a layout structure and associated file layout structure(s).
5030 */
5031APPLESTATIC void
5032nfscl_freelayout(struct nfscllayout *layp)
5033{
5034	struct nfsclflayout *flp, *nflp;
5035	struct nfsclrecalllayout *rp, *nrp;
5036
5037	LIST_FOREACH_SAFE(flp, &layp->nfsly_flayread, nfsfl_list, nflp) {
5038		LIST_REMOVE(flp, nfsfl_list);
5039		nfscl_freeflayout(flp);
5040	}
5041	LIST_FOREACH_SAFE(flp, &layp->nfsly_flayrw, nfsfl_list, nflp) {
5042		LIST_REMOVE(flp, nfsfl_list);
5043		nfscl_freeflayout(flp);
5044	}
5045	LIST_FOREACH_SAFE(rp, &layp->nfsly_recall, nfsrecly_list, nrp) {
5046		LIST_REMOVE(rp, nfsrecly_list);
5047		free(rp, M_NFSLAYRECALL);
5048	}
5049	nfscl_layoutcnt--;
5050	free(layp, M_NFSLAYOUT);
5051}
5052
5053/*
5054 * Free up a file layout structure.
5055 */
5056APPLESTATIC void
5057nfscl_freeflayout(struct nfsclflayout *flp)
5058{
5059	int i;
5060
5061	for (i = 0; i < flp->nfsfl_fhcnt; i++)
5062		free(flp->nfsfl_fh[i], M_NFSFH);
5063	if (flp->nfsfl_devp != NULL)
5064		flp->nfsfl_devp->nfsdi_layoutrefs--;
5065	free(flp, M_NFSFLAYOUT);
5066}
5067
5068/*
5069 * Free up a file layout devinfo structure.
5070 */
5071APPLESTATIC void
5072nfscl_freedevinfo(struct nfscldevinfo *dip)
5073{
5074
5075	free(dip, M_NFSDEVINFO);
5076}
5077
5078/*
5079 * Mark any layouts that match as recalled.
5080 */
5081static int
5082nfscl_layoutrecall(int recalltype, struct nfscllayout *lyp, uint32_t iomode,
5083    uint64_t off, uint64_t len, uint32_t stateseqid,
5084    struct nfsclrecalllayout *recallp)
5085{
5086	struct nfsclrecalllayout *rp, *orp;
5087
5088	recallp->nfsrecly_recalltype = recalltype;
5089	recallp->nfsrecly_iomode = iomode;
5090	recallp->nfsrecly_stateseqid = stateseqid;
5091	recallp->nfsrecly_off = off;
5092	recallp->nfsrecly_len = len;
5093	/*
5094	 * Order the list as file returns first, followed by fsid and any
5095	 * returns, both in increasing stateseqid order.
5096	 * Note that the seqids wrap around, so 1 is after 0xffffffff.
5097	 * (I'm not sure this is correct because I find RFC5661 confusing
5098	 *  on this, but hopefully it will work ok.)
5099	 */
5100	orp = NULL;
5101	LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5102		orp = rp;
5103		if ((recalltype == NFSLAYOUTRETURN_FILE &&
5104		     (rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE ||
5105		      nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) ||
5106		    (recalltype != NFSLAYOUTRETURN_FILE &&
5107		     rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE &&
5108		     nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) {
5109			LIST_INSERT_BEFORE(rp, recallp, nfsrecly_list);
5110			break;
5111		}
5112	}
5113	if (rp == NULL) {
5114		if (orp == NULL)
5115			LIST_INSERT_HEAD(&lyp->nfsly_recall, recallp,
5116			    nfsrecly_list);
5117		else
5118			LIST_INSERT_AFTER(orp, recallp, nfsrecly_list);
5119	}
5120	lyp->nfsly_flags |= NFSLY_RECALL;
5121	return (0);
5122}
5123
5124/*
5125 * Compare the two seqids for ordering. The trick is that the seqids can
5126 * wrap around from 0xffffffff->0, so check for the cases where one
5127 * has wrapped around.
5128 * Return 1 if seqid1 comes before seqid2, 0 otherwise.
5129 */
5130static int
5131nfscl_seq(uint32_t seqid1, uint32_t seqid2)
5132{
5133
5134	if (seqid2 > seqid1 && (seqid2 - seqid1) >= 0x7fffffff)
5135		/* seqid2 has wrapped around. */
5136		return (0);
5137	if (seqid1 > seqid2 && (seqid1 - seqid2) >= 0x7fffffff)
5138		/* seqid1 has wrapped around. */
5139		return (1);
5140	if (seqid1 <= seqid2)
5141		return (1);
5142	return (0);
5143}
5144
5145/*
5146 * Do a layout return for each of the recalls.
5147 */
5148static void
5149nfscl_layoutreturn(struct nfsmount *nmp, struct nfscllayout *lyp,
5150    struct ucred *cred, NFSPROC_T *p)
5151{
5152	struct nfsclrecalllayout *rp;
5153	nfsv4stateid_t stateid;
5154
5155	NFSBCOPY(lyp->nfsly_stateid.other, stateid.other, NFSX_STATEIDOTHER);
5156	LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5157		stateid.seqid = rp->nfsrecly_stateseqid;
5158		(void)nfsrpc_layoutreturn(nmp, lyp->nfsly_fh,
5159		    lyp->nfsly_fhlen, 0, NFSLAYOUT_NFSV4_1_FILES,
5160		    rp->nfsrecly_iomode, rp->nfsrecly_recalltype,
5161		    rp->nfsrecly_off, rp->nfsrecly_len,
5162		    &stateid, 0, NULL, cred, p, NULL);
5163	}
5164}
5165
5166/*
5167 * Do the layout commit for a file layout.
5168 */
5169static void
5170nfscl_dolayoutcommit(struct nfsmount *nmp, struct nfscllayout *lyp,
5171    struct ucred *cred, NFSPROC_T *p)
5172{
5173	struct nfsclflayout *flp;
5174	uint64_t len;
5175	int error;
5176
5177	LIST_FOREACH(flp, &lyp->nfsly_flayrw, nfsfl_list) {
5178		if (flp->nfsfl_off <= lyp->nfsly_lastbyte) {
5179			len = flp->nfsfl_end - flp->nfsfl_off;
5180			error = nfsrpc_layoutcommit(nmp, lyp->nfsly_fh,
5181			    lyp->nfsly_fhlen, 0, flp->nfsfl_off, len,
5182			    lyp->nfsly_lastbyte, &lyp->nfsly_stateid,
5183			    NFSLAYOUT_NFSV4_1_FILES, 0, NULL, cred, p, NULL);
5184			NFSCL_DEBUG(4, "layoutcommit err=%d\n", error);
5185			if (error == NFSERR_NOTSUPP) {
5186				/* If not supported, don't bother doing it. */
5187				NFSLOCKMNT(nmp);
5188				nmp->nm_state |= NFSSTA_NOLAYOUTCOMMIT;
5189				NFSUNLOCKMNT(nmp);
5190				break;
5191			}
5192		}
5193	}
5194}
5195
5196/*
5197 * Commit all layouts for a file (vnode).
5198 */
5199int
5200nfscl_layoutcommit(vnode_t vp, NFSPROC_T *p)
5201{
5202	struct nfsclclient *clp;
5203	struct nfscllayout *lyp;
5204	struct nfsnode *np = VTONFS(vp);
5205	mount_t mp;
5206	struct nfsmount *nmp;
5207
5208	mp = vnode_mount(vp);
5209	nmp = VFSTONFS(mp);
5210	if (NFSHASNOLAYOUTCOMMIT(nmp))
5211		return (0);
5212	NFSLOCKCLSTATE();
5213	clp = nmp->nm_clp;
5214	if (clp == NULL) {
5215		NFSUNLOCKCLSTATE();
5216		return (EPERM);
5217	}
5218	lyp = nfscl_findlayout(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5219	if (lyp == NULL) {
5220		NFSUNLOCKCLSTATE();
5221		return (EPERM);
5222	}
5223	nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
5224	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
5225		NFSUNLOCKCLSTATE();
5226		return (EPERM);
5227	}
5228tryagain:
5229	if ((lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
5230		lyp->nfsly_flags &= ~NFSLY_WRITTEN;
5231		NFSUNLOCKCLSTATE();
5232		NFSCL_DEBUG(4, "do layoutcommit2\n");
5233		nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, NFSPROCCRED(p), p);
5234		NFSLOCKCLSTATE();
5235		goto tryagain;
5236	}
5237	nfsv4_relref(&lyp->nfsly_lock);
5238	NFSUNLOCKCLSTATE();
5239	return (0);
5240}
5241
5242