1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 2009 Rick Macklem, University of Guelph
3191783Srmacklem * All rights reserved.
4191783Srmacklem *
5191783Srmacklem * Redistribution and use in source and binary forms, with or without
6191783Srmacklem * modification, are permitted provided that the following conditions
7191783Srmacklem * are met:
8191783Srmacklem * 1. Redistributions of source code must retain the above copyright
9191783Srmacklem *    notice, this list of conditions and the following disclaimer.
10191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
11191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
12191783Srmacklem *    documentation and/or other materials provided with the distribution.
13191783Srmacklem *
14191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24191783Srmacklem * SUCH DAMAGE.
25191783Srmacklem *
26191783Srmacklem */
27191783Srmacklem
28191783Srmacklem#include <sys/cdefs.h>
29191783Srmacklem__FBSDID("$FreeBSD$");
30191783Srmacklem
31191783Srmacklem/*
32191783Srmacklem * These functions implement the client side state handling for NFSv4.
33191783Srmacklem * NFSv4 state handling:
34191783Srmacklem * - A lockowner is used to determine lock contention, so it
35191783Srmacklem *   corresponds directly to a Posix pid. (1 to 1 mapping)
36191783Srmacklem * - The correct granularity of an OpenOwner is not nearly so
37191783Srmacklem *   obvious. An OpenOwner does the following:
38191783Srmacklem *   - provides a serial sequencing of Open/Close/Lock-with-new-lockowner
39198289Sjh *   - is used to check for Open/Share contention (not applicable to
40191783Srmacklem *     this client, since all Opens are Deny_None)
41198289Sjh *   As such, I considered both extreme.
42191783Srmacklem *   1 OpenOwner per ClientID - Simple to manage, but fully serializes
43191783Srmacklem *   all Open, Close and Lock (with a new lockowner) Ops.
44191783Srmacklem *   1 OpenOwner for each Open - This one results in an OpenConfirm for
45191783Srmacklem *   every Open, for most servers.
46191783Srmacklem *   So, I chose to use the same mapping as I did for LockOwnwers.
47191783Srmacklem *   The main concern here is that you can end up with multiple Opens
48191783Srmacklem *   for the same File Handle, but on different OpenOwners (opens
49191783Srmacklem *   inherited from parents, grandparents...) and you do not know
50191783Srmacklem *   which of these the vnodeop close applies to. This is handled by
51191783Srmacklem *   delaying the Close Op(s) until all of the Opens have been closed.
52191783Srmacklem *   (It is not yet obvious if this is the correct granularity.)
53198289Sjh * - How the code handles serialization:
54198289Sjh *   - For the ClientId, it uses an exclusive lock while getting its
55191783Srmacklem *     SetClientId and during recovery. Otherwise, it uses a shared
56191783Srmacklem *     lock via a reference count.
57191783Srmacklem *   - For the rest of the data structures, it uses an SMP mutex
58191783Srmacklem *     (once the nfs client is SMP safe) and doesn't sleep while
59191783Srmacklem *     manipulating the linked lists.
60191783Srmacklem *   - The serialization of Open/Close/Lock/LockU falls out in the
61191783Srmacklem *     "wash", since OpenOwners and LockOwners are both mapped from
62191783Srmacklem *     Posix pid. In other words, there is only one Posix pid using
63191783Srmacklem *     any given owner, so that owner is serialized. (If you change
64191783Srmacklem *     the granularity of the OpenOwner, then code must be added to
65191783Srmacklem *     serialize Ops on the OpenOwner.)
66191783Srmacklem * - When to get rid of OpenOwners and LockOwners.
67227744Srmacklem *   - The function nfscl_cleanup_common() is executed after a process exits.
68227744Srmacklem *     It goes through the client list looking for all Open and Lock Owners.
69191783Srmacklem *     When one is found, it is marked "defunct" or in the case of
70191783Srmacklem *     an OpenOwner without any Opens, freed.
71191783Srmacklem *     The renew thread scans for defunct Owners and gets rid of them,
72191783Srmacklem *     if it can. The LockOwners will also be deleted when the
73191783Srmacklem *     associated Open is closed.
74191783Srmacklem *   - If the LockU or Close Op(s) fail during close in a way
75191783Srmacklem *     that could be recovered upon retry, they are relinked to the
76191783Srmacklem *     ClientId's defunct open list and retried by the renew thread
77191783Srmacklem *     until they succeed or an unmount/recovery occurs.
78191783Srmacklem *     (Since we are done with them, they do not need to be recovered.)
79191783Srmacklem */
80191783Srmacklem
81191783Srmacklem#ifndef APPLEKEXT
82191783Srmacklem#include <fs/nfs/nfsport.h>
83191783Srmacklem
84191783Srmacklem/*
85191783Srmacklem * Global variables
86191783Srmacklem */
87191783Srmacklemextern struct nfsstats newnfsstats;
88191783Srmacklemextern struct nfsreqhead nfsd_reqq;
89244042Srmacklemextern u_int32_t newnfs_false, newnfs_true;
90244042Srmacklemextern int nfscl_debuglevel;
91191783SrmacklemNFSREQSPINLOCK;
92191783SrmacklemNFSCLSTATEMUTEX;
93191783Srmacklemint nfscl_inited = 0;
94191783Srmacklemstruct nfsclhead nfsclhead;	/* Head of clientid list */
95191783Srmacklemint nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
96244042Srmacklemint nfscl_layouthighwater = NFSCLLAYOUTHIGHWATER;
97191783Srmacklem#endif	/* !APPLEKEXT */
98191783Srmacklem
99191783Srmacklemstatic int nfscl_delegcnt = 0;
100244042Srmacklemstatic int nfscl_layoutcnt = 0;
101191783Srmacklemstatic int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *,
102223774Srmacklem    u_int8_t *, u_int32_t, struct nfscllockowner **, struct nfsclopen **);
103191783Srmacklemstatic void nfscl_clrelease(struct nfsclclient *);
104191783Srmacklemstatic void nfscl_cleanclient(struct nfsclclient *);
105191783Srmacklemstatic void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
106191783Srmacklem    struct ucred *, NFSPROC_T *);
107191783Srmacklemstatic int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
108191783Srmacklem    struct nfsmount *, struct ucred *, NFSPROC_T *);
109191783Srmacklemstatic void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *);
110191783Srmacklemstatic void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
111191783Srmacklem    struct nfscllock *, int);
112191783Srmacklemstatic int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
113191783Srmacklem    struct nfscllock **, int);
114191783Srmacklemstatic void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *);
115191783Srmacklemstatic u_int32_t nfscl_nextcbident(void);
116244042Srmacklemstatic mount_t nfscl_getmnt(int, uint8_t *, u_int32_t, struct nfsclclient **);
117244042Srmacklemstatic struct nfsclclient *nfscl_getclnt(u_int32_t);
118244042Srmacklemstatic struct nfsclclient *nfscl_getclntsess(uint8_t *);
119191783Srmacklemstatic struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *,
120191783Srmacklem    int);
121244042Srmacklemstatic void nfscl_retoncloselayout(struct nfsclclient *, uint8_t *, int);
122244042Srmacklemstatic void nfscl_reldevinfo_locked(struct nfscldevinfo *);
123244042Srmacklemstatic struct nfscllayout *nfscl_findlayout(struct nfsclclient *, u_int8_t *,
124244042Srmacklem    int);
125244042Srmacklemstatic struct nfscldevinfo *nfscl_finddevinfo(struct nfsclclient *, uint8_t *);
126191783Srmacklemstatic int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *,
127191783Srmacklem    u_int8_t *, struct nfscllock **);
128191783Srmacklemstatic void nfscl_freealllocks(struct nfscllockownerhead *, int);
129201439Srmacklemstatic int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int,
130201439Srmacklem    struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **);
131191783Srmacklemstatic void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *,
132191783Srmacklem    struct nfsclowner **, struct nfsclowner **, struct nfsclopen **,
133191783Srmacklem    struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *);
134191783Srmacklemstatic int nfscl_moveopen(vnode_t , struct nfsclclient *,
135191783Srmacklem    struct nfsmount *, struct nfsclopen *, struct nfsclowner *,
136191783Srmacklem    struct nfscldeleg *, struct ucred *, NFSPROC_T *);
137191783Srmacklemstatic void nfscl_totalrecall(struct nfsclclient *);
138191783Srmacklemstatic int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *,
139191783Srmacklem    struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *);
140191783Srmacklemstatic int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int,
141191783Srmacklem    u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int,
142191783Srmacklem    struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *);
143191783Srmacklemstatic int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
144191783Srmacklem    int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short,
145191783Srmacklem    struct ucred *, NFSPROC_T *);
146191783Srmacklemstatic int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
147191783Srmacklem    struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
148191783Srmacklemstatic void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *);
149191783Srmacklemstatic int nfscl_errmap(struct nfsrv_descript *);
150191783Srmacklemstatic void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
151191783Srmacklemstatic int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
152207082Srmacklem    struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int);
153191783Srmacklemstatic void nfscl_freeopenowner(struct nfsclowner *, int);
154191783Srmacklemstatic void nfscl_cleandeleg(struct nfscldeleg *);
155191783Srmacklemstatic int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
156191783Srmacklem    struct nfsmount *, NFSPROC_T *);
157228217Srmacklemstatic void nfscl_emptylockowner(struct nfscllockowner *,
158228217Srmacklem    struct nfscllockownerfhhead *);
159244042Srmacklemstatic void nfscl_mergeflayouts(struct nfsclflayouthead *,
160244042Srmacklem    struct nfsclflayouthead *);
161244042Srmacklemstatic int nfscl_layoutrecall(int, struct nfscllayout *, uint32_t, uint64_t,
162244042Srmacklem    uint64_t, uint32_t, struct nfsclrecalllayout *);
163244042Srmacklemstatic int nfscl_seq(uint32_t, uint32_t);
164244042Srmacklemstatic void nfscl_layoutreturn(struct nfsmount *, struct nfscllayout *,
165244042Srmacklem    struct ucred *, NFSPROC_T *);
166244042Srmacklemstatic void nfscl_dolayoutcommit(struct nfsmount *, struct nfscllayout *,
167244042Srmacklem    struct ucred *, NFSPROC_T *);
168191783Srmacklem
169191783Srmacklemstatic short nfscberr_null[] = {
170191783Srmacklem	0,
171191783Srmacklem	0,
172191783Srmacklem};
173191783Srmacklem
174191783Srmacklemstatic short nfscberr_getattr[] = {
175191783Srmacklem	NFSERR_RESOURCE,
176191783Srmacklem	NFSERR_BADHANDLE,
177191783Srmacklem	NFSERR_BADXDR,
178191783Srmacklem	NFSERR_RESOURCE,
179191783Srmacklem	NFSERR_SERVERFAULT,
180191783Srmacklem	0,
181191783Srmacklem};
182191783Srmacklem
183191783Srmacklemstatic short nfscberr_recall[] = {
184191783Srmacklem	NFSERR_RESOURCE,
185191783Srmacklem	NFSERR_BADHANDLE,
186191783Srmacklem	NFSERR_BADSTATEID,
187191783Srmacklem	NFSERR_BADXDR,
188191783Srmacklem	NFSERR_RESOURCE,
189191783Srmacklem	NFSERR_SERVERFAULT,
190191783Srmacklem	0,
191191783Srmacklem};
192191783Srmacklem
193191783Srmacklemstatic short *nfscl_cberrmap[] = {
194191783Srmacklem	nfscberr_null,
195191783Srmacklem	nfscberr_null,
196191783Srmacklem	nfscberr_null,
197191783Srmacklem	nfscberr_getattr,
198191783Srmacklem	nfscberr_recall
199191783Srmacklem};
200191783Srmacklem
201191783Srmacklem#define	NETFAMILY(clp) \
202191783Srmacklem		(((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET)
203191783Srmacklem
204191783Srmacklem/*
205191783Srmacklem * Called for an open operation.
206191783Srmacklem * If the nfhp argument is NULL, just get an openowner.
207191783Srmacklem */
208191783SrmacklemAPPLESTATIC int
209191783Srmacklemnfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg,
210191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp,
211191783Srmacklem    struct nfsclopen **opp, int *newonep, int *retp, int lockit)
212191783Srmacklem{
213191783Srmacklem	struct nfsclclient *clp;
214191783Srmacklem	struct nfsclowner *owp, *nowp;
215191783Srmacklem	struct nfsclopen *op = NULL, *nop = NULL;
216191783Srmacklem	struct nfscldeleg *dp;
217191783Srmacklem	struct nfsclownerhead *ohp;
218191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
219191783Srmacklem	int ret;
220191783Srmacklem
221191783Srmacklem	if (newonep != NULL)
222191783Srmacklem		*newonep = 0;
223191783Srmacklem	if (opp != NULL)
224191783Srmacklem		*opp = NULL;
225191783Srmacklem	if (owpp != NULL)
226191783Srmacklem		*owpp = NULL;
227191783Srmacklem
228191783Srmacklem	/*
229191783Srmacklem	 * Might need one or both of these, so MALLOC them now, to
230191783Srmacklem	 * avoid a tsleep() in MALLOC later.
231191783Srmacklem	 */
232191783Srmacklem	MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner),
233191783Srmacklem	    M_NFSCLOWNER, M_WAITOK);
234191783Srmacklem	if (nfhp != NULL)
235191783Srmacklem	    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
236191783Srmacklem		fhlen - 1, M_NFSCLOPEN, M_WAITOK);
237244042Srmacklem	ret = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
238191783Srmacklem	if (ret != 0) {
239191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
240191783Srmacklem		if (nop != NULL)
241191783Srmacklem			FREE((caddr_t)nop, M_NFSCLOPEN);
242191783Srmacklem		return (ret);
243191783Srmacklem	}
244191783Srmacklem
245191783Srmacklem	/*
246191783Srmacklem	 * Get the Open iff it already exists.
247191783Srmacklem	 * If none found, add the new one or return error, depending upon
248191783Srmacklem	 * "create".
249191783Srmacklem	 */
250222719Srmacklem	nfscl_filllockowner(p->td_proc, own, F_POSIX);
251191783Srmacklem	NFSLOCKCLSTATE();
252191783Srmacklem	dp = NULL;
253191783Srmacklem	/* First check the delegation list */
254191783Srmacklem	if (nfhp != NULL && usedeleg) {
255191783Srmacklem		LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
256191783Srmacklem			if (dp->nfsdl_fhlen == fhlen &&
257191783Srmacklem			    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
258191783Srmacklem				if (!(amode & NFSV4OPEN_ACCESSWRITE) ||
259191783Srmacklem				    (dp->nfsdl_flags & NFSCLDL_WRITE))
260191783Srmacklem					break;
261191783Srmacklem				dp = NULL;
262191783Srmacklem				break;
263191783Srmacklem			}
264191783Srmacklem		}
265191783Srmacklem	}
266191783Srmacklem
267191783Srmacklem	if (dp != NULL)
268191783Srmacklem		ohp = &dp->nfsdl_owner;
269191783Srmacklem	else
270191783Srmacklem		ohp = &clp->nfsc_owner;
271191783Srmacklem	/* Now, search for an openowner */
272191783Srmacklem	LIST_FOREACH(owp, ohp, nfsow_list) {
273191783Srmacklem		if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN))
274191783Srmacklem			break;
275191783Srmacklem	}
276191783Srmacklem
277191783Srmacklem	/*
278191783Srmacklem	 * Create a new open, as required.
279191783Srmacklem	 */
280191783Srmacklem	nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen,
281191783Srmacklem	    newonep);
282191783Srmacklem
283191783Srmacklem	/*
284191783Srmacklem	 * Serialize modifications to the open owner for multiple threads
285191783Srmacklem	 * within the same process using a read/write sleep lock.
286191783Srmacklem	 */
287191783Srmacklem	if (lockit)
288191783Srmacklem		nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
289191783Srmacklem	NFSUNLOCKCLSTATE();
290191783Srmacklem	if (nowp != NULL)
291191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
292191783Srmacklem	if (nop != NULL)
293191783Srmacklem		FREE((caddr_t)nop, M_NFSCLOPEN);
294191783Srmacklem	if (owpp != NULL)
295191783Srmacklem		*owpp = owp;
296191783Srmacklem	if (opp != NULL)
297191783Srmacklem		*opp = op;
298206688Srmacklem	if (retp != NULL) {
299206688Srmacklem		if (nfhp != NULL && dp != NULL && nop == NULL)
300206688Srmacklem			/* new local open on delegation */
301206688Srmacklem			*retp = NFSCLOPEN_SETCRED;
302206688Srmacklem		else
303206688Srmacklem			*retp = NFSCLOPEN_OK;
304206688Srmacklem	}
305191783Srmacklem
306191783Srmacklem	/*
307191783Srmacklem	 * Now, check the mode on the open and return the appropriate
308191783Srmacklem	 * value.
309191783Srmacklem	 */
310191783Srmacklem	if (op != NULL && (amode & ~(op->nfso_mode))) {
311191783Srmacklem		op->nfso_mode |= amode;
312191783Srmacklem		if (retp != NULL && dp == NULL)
313191783Srmacklem			*retp = NFSCLOPEN_DOOPEN;
314191783Srmacklem	}
315191783Srmacklem	return (0);
316191783Srmacklem}
317191783Srmacklem
318191783Srmacklem/*
319191783Srmacklem * Create a new open, as required.
320191783Srmacklem */
321191783Srmacklemstatic void
322191783Srmacklemnfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp,
323191783Srmacklem    struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp,
324191783Srmacklem    struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen,
325191783Srmacklem    int *newonep)
326191783Srmacklem{
327191783Srmacklem	struct nfsclowner *owp = *owpp, *nowp;
328191783Srmacklem	struct nfsclopen *op, *nop;
329191783Srmacklem
330191783Srmacklem	if (nowpp != NULL)
331191783Srmacklem		nowp = *nowpp;
332191783Srmacklem	else
333191783Srmacklem		nowp = NULL;
334191783Srmacklem	if (nopp != NULL)
335191783Srmacklem		nop = *nopp;
336191783Srmacklem	else
337191783Srmacklem		nop = NULL;
338191783Srmacklem	if (owp == NULL && nowp != NULL) {
339191783Srmacklem		NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
340191783Srmacklem		LIST_INIT(&nowp->nfsow_open);
341191783Srmacklem		nowp->nfsow_clp = clp;
342191783Srmacklem		nowp->nfsow_seqid = 0;
343191783Srmacklem		nowp->nfsow_defunct = 0;
344191783Srmacklem		nfscl_lockinit(&nowp->nfsow_rwlock);
345191783Srmacklem		if (dp != NULL) {
346191783Srmacklem			newnfsstats.cllocalopenowners++;
347191783Srmacklem			LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list);
348191783Srmacklem		} else {
349191783Srmacklem			newnfsstats.clopenowners++;
350191783Srmacklem			LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list);
351191783Srmacklem		}
352191783Srmacklem		owp = *owpp = nowp;
353191783Srmacklem		*nowpp = NULL;
354191783Srmacklem		if (newonep != NULL)
355191783Srmacklem			*newonep = 1;
356191783Srmacklem	}
357191783Srmacklem
358191783Srmacklem	 /* If an fhp has been specified, create an Open as well. */
359191783Srmacklem	if (fhp != NULL) {
360191783Srmacklem		/* and look for the correct open, based upon FH */
361191783Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
362191783Srmacklem			if (op->nfso_fhlen == fhlen &&
363191783Srmacklem			    !NFSBCMP(op->nfso_fh, fhp, fhlen))
364191783Srmacklem				break;
365191783Srmacklem		}
366191783Srmacklem		if (op == NULL && nop != NULL) {
367191783Srmacklem			nop->nfso_own = owp;
368191783Srmacklem			nop->nfso_mode = 0;
369191783Srmacklem			nop->nfso_opencnt = 0;
370191783Srmacklem			nop->nfso_posixlock = 1;
371191783Srmacklem			nop->nfso_fhlen = fhlen;
372191783Srmacklem			NFSBCOPY(fhp, nop->nfso_fh, fhlen);
373191783Srmacklem			LIST_INIT(&nop->nfso_lock);
374191783Srmacklem			nop->nfso_stateid.seqid = 0;
375191783Srmacklem			nop->nfso_stateid.other[0] = 0;
376191783Srmacklem			nop->nfso_stateid.other[1] = 0;
377191783Srmacklem			nop->nfso_stateid.other[2] = 0;
378191783Srmacklem			if (dp != NULL) {
379191783Srmacklem				TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
380191783Srmacklem				TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
381191783Srmacklem				    nfsdl_list);
382191783Srmacklem				dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
383191783Srmacklem				newnfsstats.cllocalopens++;
384191783Srmacklem			} else {
385191783Srmacklem				newnfsstats.clopens++;
386191783Srmacklem			}
387191783Srmacklem			LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list);
388191783Srmacklem			*opp = nop;
389191783Srmacklem			*nopp = NULL;
390191783Srmacklem			if (newonep != NULL)
391191783Srmacklem				*newonep = 1;
392191783Srmacklem		} else {
393191783Srmacklem			*opp = op;
394191783Srmacklem		}
395191783Srmacklem	}
396191783Srmacklem}
397191783Srmacklem
398191783Srmacklem/*
399191783Srmacklem * Called to find/add a delegation to a client.
400191783Srmacklem */
401191783SrmacklemAPPLESTATIC int
402191783Srmacklemnfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
403191783Srmacklem    int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp)
404191783Srmacklem{
405191783Srmacklem	struct nfscldeleg *dp = *dpp, *tdp;
406191783Srmacklem
407191783Srmacklem	/*
408191783Srmacklem	 * First, if we have received a Read delegation for a file on a
409191783Srmacklem	 * read/write file system, just return it, because they aren't
410191783Srmacklem	 * useful, imho.
411191783Srmacklem	 */
412191783Srmacklem	if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) &&
413191783Srmacklem	    (dp->nfsdl_flags & NFSCLDL_READ)) {
414191783Srmacklem		(void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p);
415191783Srmacklem		FREE((caddr_t)dp, M_NFSCLDELEG);
416191783Srmacklem		*dpp = NULL;
417191783Srmacklem		return (0);
418191783Srmacklem	}
419191783Srmacklem
420191783Srmacklem	/* Look for the correct deleg, based upon FH */
421191783Srmacklem	NFSLOCKCLSTATE();
422191783Srmacklem	tdp = nfscl_finddeleg(clp, nfhp, fhlen);
423191783Srmacklem	if (tdp == NULL) {
424191783Srmacklem		if (dp == NULL) {
425191783Srmacklem			NFSUNLOCKCLSTATE();
426191783Srmacklem			return (NFSERR_BADSTATEID);
427191783Srmacklem		}
428191783Srmacklem		*dpp = NULL;
429191783Srmacklem		TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
430191783Srmacklem		LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp,
431191783Srmacklem		    nfsdl_hash);
432191783Srmacklem		dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
433191783Srmacklem		newnfsstats.cldelegates++;
434191783Srmacklem		nfscl_delegcnt++;
435191783Srmacklem	} else {
436191783Srmacklem		/*
437191783Srmacklem		 * Delegation already exists, what do we do if a new one??
438191783Srmacklem		 */
439191783Srmacklem		if (dp != NULL) {
440191783Srmacklem			printf("Deleg already exists!\n");
441191783Srmacklem			FREE((caddr_t)dp, M_NFSCLDELEG);
442191783Srmacklem			*dpp = NULL;
443191783Srmacklem		} else {
444191783Srmacklem			*dpp = tdp;
445191783Srmacklem		}
446191783Srmacklem	}
447191783Srmacklem	NFSUNLOCKCLSTATE();
448191783Srmacklem	return (0);
449191783Srmacklem}
450191783Srmacklem
451191783Srmacklem/*
452191783Srmacklem * Find a delegation for this file handle. Return NULL upon failure.
453191783Srmacklem */
454191783Srmacklemstatic struct nfscldeleg *
455191783Srmacklemnfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
456191783Srmacklem{
457191783Srmacklem	struct nfscldeleg *dp;
458191783Srmacklem
459191783Srmacklem	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) {
460191783Srmacklem	    if (dp->nfsdl_fhlen == fhlen &&
461191783Srmacklem		!NFSBCMP(dp->nfsdl_fh, fhp, fhlen))
462191783Srmacklem		break;
463191783Srmacklem	}
464191783Srmacklem	return (dp);
465191783Srmacklem}
466191783Srmacklem
467191783Srmacklem/*
468191783Srmacklem * Get a stateid for an I/O operation. First, look for an open and iff
469191783Srmacklem * found, return either a lockowner stateid or the open stateid.
470191783Srmacklem * If no Open is found, just return error and the special stateid of all zeros.
471191783Srmacklem */
472191783SrmacklemAPPLESTATIC int
473191783Srmacklemnfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
474244042Srmacklem    int fords, struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp,
475191783Srmacklem    void **lckpp)
476191783Srmacklem{
477191783Srmacklem	struct nfsclclient *clp;
478191783Srmacklem	struct nfsclowner *owp;
479195510Srmacklem	struct nfsclopen *op = NULL;
480191783Srmacklem	struct nfscllockowner *lp;
481191783Srmacklem	struct nfscldeleg *dp;
482191783Srmacklem	struct nfsnode *np;
483191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
484191783Srmacklem	int error, done;
485191783Srmacklem
486191783Srmacklem	*lckpp = NULL;
487191783Srmacklem	/*
488191783Srmacklem	 * Initially, just set the special stateid of all zeros.
489244042Srmacklem	 * (Don't do this for a DS, since the special stateid can't be used.)
490191783Srmacklem	 */
491244042Srmacklem	if (fords == 0) {
492244042Srmacklem		stateidp->seqid = 0;
493244042Srmacklem		stateidp->other[0] = 0;
494244042Srmacklem		stateidp->other[1] = 0;
495244042Srmacklem		stateidp->other[2] = 0;
496244042Srmacklem	}
497191783Srmacklem	if (vnode_vtype(vp) != VREG)
498191783Srmacklem		return (EISDIR);
499191783Srmacklem	np = VTONFS(vp);
500191783Srmacklem	NFSLOCKCLSTATE();
501191783Srmacklem	clp = nfscl_findcl(VFSTONFS(vnode_mount(vp)));
502191783Srmacklem	if (clp == NULL) {
503191783Srmacklem		NFSUNLOCKCLSTATE();
504191783Srmacklem		return (EACCES);
505191783Srmacklem	}
506191783Srmacklem
507191783Srmacklem	/*
508206818Srmacklem	 * Wait for recovery to complete.
509206818Srmacklem	 */
510206818Srmacklem	while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG))
511206818Srmacklem		(void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR,
512206818Srmacklem		    PZERO, "nfsrecvr", NULL);
513206818Srmacklem
514206818Srmacklem	/*
515191783Srmacklem	 * First, look for a delegation.
516191783Srmacklem	 */
517191783Srmacklem	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
518191783Srmacklem		if (dp->nfsdl_fhlen == fhlen &&
519191783Srmacklem		    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
520191783Srmacklem			if (!(mode & NFSV4OPEN_ACCESSWRITE) ||
521191783Srmacklem			    (dp->nfsdl_flags & NFSCLDL_WRITE)) {
522191783Srmacklem				stateidp->seqid = dp->nfsdl_stateid.seqid;
523191783Srmacklem				stateidp->other[0] = dp->nfsdl_stateid.other[0];
524191783Srmacklem				stateidp->other[1] = dp->nfsdl_stateid.other[1];
525191783Srmacklem				stateidp->other[2] = dp->nfsdl_stateid.other[2];
526191783Srmacklem				if (!(np->n_flag & NDELEGRECALL)) {
527191783Srmacklem					TAILQ_REMOVE(&clp->nfsc_deleg, dp,
528191783Srmacklem					    nfsdl_list);
529191783Srmacklem					TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
530191783Srmacklem					    nfsdl_list);
531191783Srmacklem					dp->nfsdl_timestamp = NFSD_MONOSEC +
532191783Srmacklem					    120;
533191783Srmacklem					dp->nfsdl_rwlock.nfslock_usecnt++;
534191783Srmacklem					*lckpp = (void *)&dp->nfsdl_rwlock;
535191783Srmacklem				}
536191783Srmacklem				NFSUNLOCKCLSTATE();
537191783Srmacklem				return (0);
538191783Srmacklem			}
539191783Srmacklem			break;
540191783Srmacklem		}
541191783Srmacklem	}
542191783Srmacklem
543191783Srmacklem	if (p != NULL) {
544191783Srmacklem		/*
545191783Srmacklem		 * If p != NULL, we want to search the parentage tree
546191783Srmacklem		 * for a matching OpenOwner and use that.
547191783Srmacklem		 */
548222719Srmacklem		nfscl_filllockowner(p->td_proc, own, F_POSIX);
549223774Srmacklem		lp = NULL;
550223774Srmacklem		error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, own, own,
551223774Srmacklem		    mode, &lp, &op);
552244042Srmacklem		if (error == 0 && lp != NULL && fords == 0) {
553244042Srmacklem			/* Don't return a lock stateid for a DS. */
554223774Srmacklem			stateidp->seqid =
555223774Srmacklem			    lp->nfsl_stateid.seqid;
556223774Srmacklem			stateidp->other[0] =
557223774Srmacklem			    lp->nfsl_stateid.other[0];
558223774Srmacklem			stateidp->other[1] =
559223774Srmacklem			    lp->nfsl_stateid.other[1];
560223774Srmacklem			stateidp->other[2] =
561223774Srmacklem			    lp->nfsl_stateid.other[2];
562223774Srmacklem			NFSUNLOCKCLSTATE();
563223774Srmacklem			return (0);
564191783Srmacklem		}
565195510Srmacklem	}
566195510Srmacklem	if (op == NULL) {
567195510Srmacklem		/* If not found, just look for any OpenOwner that will work. */
568191783Srmacklem		done = 0;
569191783Srmacklem		owp = LIST_FIRST(&clp->nfsc_owner);
570191783Srmacklem		while (!done && owp != NULL) {
571195510Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
572195510Srmacklem				if (op->nfso_fhlen == fhlen &&
573195510Srmacklem				    !NFSBCMP(op->nfso_fh, nfhp, fhlen) &&
574195510Srmacklem				    (mode & op->nfso_mode) == mode) {
575195510Srmacklem					done = 1;
576195510Srmacklem					break;
577195510Srmacklem				}
578191783Srmacklem			}
579195510Srmacklem			if (!done)
580195510Srmacklem				owp = LIST_NEXT(owp, nfsow_list);
581191783Srmacklem		}
582191783Srmacklem		if (!done) {
583191783Srmacklem			NFSUNLOCKCLSTATE();
584191783Srmacklem			return (ENOENT);
585191783Srmacklem		}
586231133Srmacklem		/*
587231133Srmacklem		 * For read aheads or write behinds, use the open cred.
588231133Srmacklem		 * A read ahead or write behind is indicated by p == NULL.
589231133Srmacklem		 */
590231133Srmacklem		if (p == NULL)
591231133Srmacklem			newnfs_copycred(&op->nfso_cred, cred);
592191783Srmacklem	}
593191783Srmacklem
594191783Srmacklem	/*
595191783Srmacklem	 * No lock stateid, so return the open stateid.
596191783Srmacklem	 */
597191783Srmacklem	stateidp->seqid = op->nfso_stateid.seqid;
598191783Srmacklem	stateidp->other[0] = op->nfso_stateid.other[0];
599191783Srmacklem	stateidp->other[1] = op->nfso_stateid.other[1];
600191783Srmacklem	stateidp->other[2] = op->nfso_stateid.other[2];
601191783Srmacklem	NFSUNLOCKCLSTATE();
602191783Srmacklem	return (0);
603191783Srmacklem}
604191783Srmacklem
605191783Srmacklem/*
606223774Srmacklem * Search for a matching file, mode and, optionally, lockowner.
607191783Srmacklem */
608191783Srmacklemstatic int
609191783Srmacklemnfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen,
610223774Srmacklem    u_int8_t *openown, u_int8_t *lockown, u_int32_t mode,
611223774Srmacklem    struct nfscllockowner **lpp, struct nfsclopen **opp)
612191783Srmacklem{
613223774Srmacklem	struct nfsclowner *owp;
614223774Srmacklem	struct nfsclopen *op, *rop, *rop2;
615223774Srmacklem	struct nfscllockowner *lp;
616223774Srmacklem	int keep_looping;
617191783Srmacklem
618223774Srmacklem	if (lpp != NULL)
619223774Srmacklem		*lpp = NULL;
620223774Srmacklem	/*
621223774Srmacklem	 * rop will be set to the open to be returned. There are three
622223774Srmacklem	 * variants of this, all for an open of the correct file:
623223774Srmacklem	 * 1 - A match of lockown.
624223774Srmacklem	 * 2 - A match of the openown, when no lockown match exists.
625223774Srmacklem	 * 3 - A match for any open, if no openown or lockown match exists.
626223774Srmacklem	 * Looking for #2 over #3 probably isn't necessary, but since
627223774Srmacklem	 * RFC3530 is vague w.r.t. the relationship between openowners and
628223774Srmacklem	 * lockowners, I think this is the safer way to go.
629223774Srmacklem	 */
630223774Srmacklem	rop = NULL;
631223774Srmacklem	rop2 = NULL;
632223774Srmacklem	keep_looping = 1;
633223774Srmacklem	/* Search the client list */
634223774Srmacklem	owp = LIST_FIRST(ohp);
635223774Srmacklem	while (owp != NULL && keep_looping != 0) {
636223774Srmacklem		/* and look for the correct open */
637223774Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
638223774Srmacklem		while (op != NULL && keep_looping != 0) {
639223774Srmacklem			if (op->nfso_fhlen == fhlen &&
640223774Srmacklem			    !NFSBCMP(op->nfso_fh, nfhp, fhlen)
641223774Srmacklem			    && (op->nfso_mode & mode) == mode) {
642223774Srmacklem				if (lpp != NULL) {
643223774Srmacklem					/* Now look for a matching lockowner. */
644223774Srmacklem					LIST_FOREACH(lp, &op->nfso_lock,
645223774Srmacklem					    nfsl_list) {
646223774Srmacklem						if (!NFSBCMP(lp->nfsl_owner,
647223774Srmacklem						    lockown,
648223774Srmacklem						    NFSV4CL_LOCKNAMELEN)) {
649223774Srmacklem							*lpp = lp;
650223774Srmacklem							rop = op;
651223774Srmacklem							keep_looping = 0;
652223774Srmacklem							break;
653223774Srmacklem						}
654223774Srmacklem					}
655191783Srmacklem				}
656223774Srmacklem				if (rop == NULL && !NFSBCMP(owp->nfsow_owner,
657223774Srmacklem				    openown, NFSV4CL_LOCKNAMELEN)) {
658223774Srmacklem					rop = op;
659223774Srmacklem					if (lpp == NULL)
660223774Srmacklem						keep_looping = 0;
661223774Srmacklem				}
662223774Srmacklem				if (rop2 == NULL)
663223774Srmacklem					rop2 = op;
664191783Srmacklem			}
665223774Srmacklem			op = LIST_NEXT(op, nfso_list);
666191783Srmacklem		}
667223774Srmacklem		owp = LIST_NEXT(owp, nfsow_list);
668191783Srmacklem	}
669223774Srmacklem	if (rop == NULL)
670223774Srmacklem		rop = rop2;
671223774Srmacklem	if (rop == NULL)
672191783Srmacklem		return (EBADF);
673223774Srmacklem	*opp = rop;
674191783Srmacklem	return (0);
675191783Srmacklem}
676191783Srmacklem
677191783Srmacklem/*
678191783Srmacklem * Release use of an open owner. Called when open operations are done
679191783Srmacklem * with the open owner.
680191783Srmacklem */
681191783SrmacklemAPPLESTATIC void
682191783Srmacklemnfscl_ownerrelease(struct nfsclowner *owp, __unused int error,
683191783Srmacklem    __unused int candelete, int unlocked)
684191783Srmacklem{
685191783Srmacklem
686191783Srmacklem	if (owp == NULL)
687191783Srmacklem		return;
688191783Srmacklem	NFSLOCKCLSTATE();
689191783Srmacklem	if (!unlocked)
690191783Srmacklem		nfscl_lockunlock(&owp->nfsow_rwlock);
691191783Srmacklem	nfscl_clrelease(owp->nfsow_clp);
692191783Srmacklem	NFSUNLOCKCLSTATE();
693191783Srmacklem}
694191783Srmacklem
695191783Srmacklem/*
696191783Srmacklem * Release use of an open structure under an open owner.
697191783Srmacklem */
698191783SrmacklemAPPLESTATIC void
699191783Srmacklemnfscl_openrelease(struct nfsclopen *op, int error, int candelete)
700191783Srmacklem{
701191783Srmacklem	struct nfsclclient *clp;
702191783Srmacklem	struct nfsclowner *owp;
703191783Srmacklem
704191783Srmacklem	if (op == NULL)
705191783Srmacklem		return;
706191783Srmacklem	NFSLOCKCLSTATE();
707191783Srmacklem	owp = op->nfso_own;
708191783Srmacklem	nfscl_lockunlock(&owp->nfsow_rwlock);
709191783Srmacklem	clp = owp->nfsow_clp;
710191783Srmacklem	if (error && candelete && op->nfso_opencnt == 0)
711191783Srmacklem		nfscl_freeopen(op, 0);
712191783Srmacklem	nfscl_clrelease(clp);
713191783Srmacklem	NFSUNLOCKCLSTATE();
714191783Srmacklem}
715191783Srmacklem
716191783Srmacklem/*
717191783Srmacklem * Called to get a clientid structure. It will optionally lock the
718191783Srmacklem * client data structures to do the SetClientId/SetClientId_confirm,
719191783Srmacklem * but will release that lock and return the clientid with a refernce
720191783Srmacklem * count on it.
721193735Srmacklem * If the "cred" argument is NULL, a new clientid should not be created.
722193735Srmacklem * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot
723193735Srmacklem * be done.
724244042Srmacklem * The start_renewthread argument tells nfscl_getcl() to start a renew
725244042Srmacklem * thread if this creates a new clp.
726192337Srmacklem * It always clpp with a reference count on it, unless returning an error.
727191783Srmacklem */
728191783SrmacklemAPPLESTATIC int
729244042Srmacklemnfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
730244042Srmacklem    int start_renewthread, struct nfsclclient **clpp)
731191783Srmacklem{
732191783Srmacklem	struct nfsclclient *clp;
733193735Srmacklem	struct nfsclclient *newclp = NULL;
734222389Srmacklem	struct nfsmount *nmp;
735193066Sjamie	char uuid[HOSTUUIDLEN];
736191783Srmacklem	int igotlock = 0, error, trystalecnt, clidinusedelay, i;
737193735Srmacklem	u_int16_t idlen = 0;
738191783Srmacklem
739222389Srmacklem	nmp = VFSTONFS(mp);
740193735Srmacklem	if (cred != NULL) {
741194117Sjamie		getcredhostuuid(cred, uuid, sizeof uuid);
742193735Srmacklem		idlen = strlen(uuid);
743193735Srmacklem		if (idlen > 0)
744193735Srmacklem			idlen += sizeof (u_int64_t);
745193735Srmacklem		else
746193735Srmacklem			idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
747193735Srmacklem		MALLOC(newclp, struct nfsclclient *,
748193735Srmacklem		    sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
749244042Srmacklem		    M_WAITOK | M_ZERO);
750193735Srmacklem	}
751191783Srmacklem	NFSLOCKCLSTATE();
752222389Srmacklem	/*
753222389Srmacklem	 * If a forced dismount is already in progress, don't
754222389Srmacklem	 * allocate a new clientid and get out now. For the case where
755222389Srmacklem	 * clp != NULL, this is a harmless optimization.
756222389Srmacklem	 */
757222389Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
758222389Srmacklem		NFSUNLOCKCLSTATE();
759222389Srmacklem		if (newclp != NULL)
760222389Srmacklem			free(newclp, M_NFSCLCLIENT);
761222389Srmacklem		return (EBADF);
762222389Srmacklem	}
763191783Srmacklem	clp = nmp->nm_clp;
764191783Srmacklem	if (clp == NULL) {
765193735Srmacklem		if (newclp == NULL) {
766193735Srmacklem			NFSUNLOCKCLSTATE();
767193735Srmacklem			return (EACCES);
768193735Srmacklem		}
769191783Srmacklem		clp = newclp;
770191783Srmacklem		clp->nfsc_idlen = idlen;
771191783Srmacklem		LIST_INIT(&clp->nfsc_owner);
772191783Srmacklem		TAILQ_INIT(&clp->nfsc_deleg);
773244042Srmacklem		TAILQ_INIT(&clp->nfsc_layout);
774244042Srmacklem		LIST_INIT(&clp->nfsc_devinfo);
775191783Srmacklem		for (i = 0; i < NFSCLDELEGHASHSIZE; i++)
776191783Srmacklem			LIST_INIT(&clp->nfsc_deleghash[i]);
777244042Srmacklem		for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++)
778244042Srmacklem			LIST_INIT(&clp->nfsc_layouthash[i]);
779191783Srmacklem		clp->nfsc_flags = NFSCLFLAGS_INITED;
780191783Srmacklem		clp->nfsc_clientidrev = 1;
781191783Srmacklem		clp->nfsc_cbident = nfscl_nextcbident();
782193066Sjamie		nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id,
783191783Srmacklem		    clp->nfsc_idlen);
784191783Srmacklem		LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list);
785191783Srmacklem		nmp->nm_clp = clp;
786191783Srmacklem		clp->nfsc_nmp = nmp;
787191783Srmacklem		NFSUNLOCKCLSTATE();
788244042Srmacklem		if (start_renewthread != 0)
789244042Srmacklem			nfscl_start_renewthread(clp);
790191783Srmacklem	} else {
791191783Srmacklem		NFSUNLOCKCLSTATE();
792193735Srmacklem		if (newclp != NULL)
793244042Srmacklem			free(newclp, M_NFSCLCLIENT);
794191783Srmacklem	}
795191783Srmacklem	NFSLOCKCLSTATE();
796223971Srmacklem	while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock &&
797223971Srmacklem	    (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0)
798191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
799222389Srmacklem		    NFSCLSTATEMUTEXPTR, mp);
800191783Srmacklem	if (!igotlock)
801222389Srmacklem		nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
802222389Srmacklem	if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
803222389Srmacklem		/*
804222389Srmacklem		 * Both nfsv4_lock() and nfsv4_getref() know to check
805222389Srmacklem		 * for MNTK_UNMOUNTF and return without sleeping to
806222389Srmacklem		 * wait for the exclusive lock to be released, since it
807222389Srmacklem		 * might be held by nfscl_umount() and we need to get out
808222389Srmacklem		 * now for that case and not wait until nfscl_umount()
809222389Srmacklem		 * releases it.
810222389Srmacklem		 */
811222389Srmacklem		NFSUNLOCKCLSTATE();
812222389Srmacklem		return (EBADF);
813222389Srmacklem	}
814191783Srmacklem	NFSUNLOCKCLSTATE();
815191783Srmacklem
816191783Srmacklem	/*
817191783Srmacklem	 * If it needs a clientid, do the setclientid now.
818191783Srmacklem	 */
819191783Srmacklem	if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) {
820191783Srmacklem		if (!igotlock)
821191783Srmacklem			panic("nfscl_clget");
822193735Srmacklem		if (p == NULL || cred == NULL) {
823191783Srmacklem			NFSLOCKCLSTATE();
824191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
825191783Srmacklem			NFSUNLOCKCLSTATE();
826191783Srmacklem			return (EACCES);
827191783Srmacklem		}
828191783Srmacklem		/*
829191783Srmacklem		 * If RFC3530 Sec. 14.2.33 is taken literally,
830191783Srmacklem		 * NFSERR_CLIDINUSE will be returned persistently for the
831191783Srmacklem		 * case where a new mount of the same file system is using
832191783Srmacklem		 * a different principal. In practice, NFSERR_CLIDINUSE is
833191783Srmacklem		 * only returned when there is outstanding unexpired state
834191783Srmacklem		 * on the clientid. As such, try for twice the lease
835191783Srmacklem		 * interval, if we know what that is. Otherwise, make a
836191783Srmacklem		 * wild ass guess.
837191783Srmacklem		 * The case of returning NFSERR_STALECLIENTID is far less
838191783Srmacklem		 * likely, but might occur if there is a significant delay
839191783Srmacklem		 * between doing the SetClientID and SetClientIDConfirm Ops,
840191783Srmacklem		 * such that the server throws away the clientid before
841191783Srmacklem		 * receiving the SetClientIDConfirm.
842191783Srmacklem		 */
843191783Srmacklem		if (clp->nfsc_renew > 0)
844191783Srmacklem			clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
845191783Srmacklem		else
846191783Srmacklem			clidinusedelay = 120;
847191783Srmacklem		trystalecnt = 3;
848191783Srmacklem		do {
849244042Srmacklem			error = nfsrpc_setclient(nmp, clp, 0, cred, p);
850191783Srmacklem			if (error == NFSERR_STALECLIENTID ||
851191783Srmacklem			    error == NFSERR_STALEDONTRECOVER ||
852244042Srmacklem			    error == NFSERR_BADSESSION ||
853191783Srmacklem			    error == NFSERR_CLIDINUSE) {
854207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfs_setcl");
855191783Srmacklem			}
856191783Srmacklem		} while (((error == NFSERR_STALECLIENTID ||
857244042Srmacklem		     error == NFSERR_BADSESSION ||
858191783Srmacklem		     error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) ||
859191783Srmacklem		    (error == NFSERR_CLIDINUSE && --clidinusedelay > 0));
860191783Srmacklem		if (error) {
861191783Srmacklem			NFSLOCKCLSTATE();
862191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
863191783Srmacklem			NFSUNLOCKCLSTATE();
864191783Srmacklem			return (error);
865191783Srmacklem		}
866191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
867191783Srmacklem	}
868191783Srmacklem	if (igotlock) {
869191783Srmacklem		NFSLOCKCLSTATE();
870191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 1);
871191783Srmacklem		NFSUNLOCKCLSTATE();
872191783Srmacklem	}
873191783Srmacklem
874191783Srmacklem	*clpp = clp;
875191783Srmacklem	return (0);
876191783Srmacklem}
877191783Srmacklem
878191783Srmacklem/*
879191783Srmacklem * Get a reference to a clientid and return it, if valid.
880191783Srmacklem */
881191783SrmacklemAPPLESTATIC struct nfsclclient *
882191783Srmacklemnfscl_findcl(struct nfsmount *nmp)
883191783Srmacklem{
884191783Srmacklem	struct nfsclclient *clp;
885191783Srmacklem
886191783Srmacklem	clp = nmp->nm_clp;
887191783Srmacklem	if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID))
888191783Srmacklem		return (NULL);
889191783Srmacklem	return (clp);
890191783Srmacklem}
891191783Srmacklem
892191783Srmacklem/*
893191783Srmacklem * Release the clientid structure. It may be locked or reference counted.
894191783Srmacklem */
895191783Srmacklemstatic void
896191783Srmacklemnfscl_clrelease(struct nfsclclient *clp)
897191783Srmacklem{
898191783Srmacklem
899191783Srmacklem	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
900191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
901191783Srmacklem	else
902191783Srmacklem		nfsv4_relref(&clp->nfsc_lock);
903191783Srmacklem}
904191783Srmacklem
905191783Srmacklem/*
906191783Srmacklem * External call for nfscl_clrelease.
907191783Srmacklem */
908191783SrmacklemAPPLESTATIC void
909191783Srmacklemnfscl_clientrelease(struct nfsclclient *clp)
910191783Srmacklem{
911191783Srmacklem
912191783Srmacklem	NFSLOCKCLSTATE();
913191783Srmacklem	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
914191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
915191783Srmacklem	else
916191783Srmacklem		nfsv4_relref(&clp->nfsc_lock);
917191783Srmacklem	NFSUNLOCKCLSTATE();
918191783Srmacklem}
919191783Srmacklem
920191783Srmacklem/*
921191783Srmacklem * Called when wanting to lock a byte region.
922191783Srmacklem */
923191783SrmacklemAPPLESTATIC int
924191783Srmacklemnfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
925191783Srmacklem    short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp,
926222719Srmacklem    int recovery, void *id, int flags, u_int8_t *rownp, u_int8_t *ropenownp,
927191783Srmacklem    struct nfscllockowner **lpp, int *newonep, int *donelocallyp)
928191783Srmacklem{
929191783Srmacklem	struct nfscllockowner *lp;
930191783Srmacklem	struct nfsclopen *op;
931191783Srmacklem	struct nfsclclient *clp;
932191783Srmacklem	struct nfscllockowner *nlp;
933191783Srmacklem	struct nfscllock *nlop, *otherlop;
934191783Srmacklem	struct nfscldeleg *dp = NULL, *ldp = NULL;
935191783Srmacklem	struct nfscllockownerhead *lhp = NULL;
936191783Srmacklem	struct nfsnode *np;
937223774Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp, openown[NFSV4CL_LOCKNAMELEN];
938223774Srmacklem	u_int8_t *openownp;
939191783Srmacklem	int error = 0, ret, donelocally = 0;
940191783Srmacklem	u_int32_t mode;
941191783Srmacklem
942223774Srmacklem	/* For Lock Ops, the open mode doesn't matter, so use 0 to match any. */
943223774Srmacklem	mode = 0;
944191783Srmacklem	np = VTONFS(vp);
945191783Srmacklem	*lpp = NULL;
946223774Srmacklem	lp = NULL;
947191783Srmacklem	*newonep = 0;
948191783Srmacklem	*donelocallyp = 0;
949191783Srmacklem
950191783Srmacklem	/*
951191783Srmacklem	 * Might need these, so MALLOC them now, to
952191783Srmacklem	 * avoid a tsleep() in MALLOC later.
953191783Srmacklem	 */
954191783Srmacklem	MALLOC(nlp, struct nfscllockowner *,
955191783Srmacklem	    sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK);
956191783Srmacklem	MALLOC(otherlop, struct nfscllock *,
957191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
958191783Srmacklem	MALLOC(nlop, struct nfscllock *,
959191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
960191783Srmacklem	nlop->nfslo_type = type;
961191783Srmacklem	nlop->nfslo_first = off;
962191783Srmacklem	if (len == NFS64BITSSET) {
963191783Srmacklem		nlop->nfslo_end = NFS64BITSSET;
964191783Srmacklem	} else {
965191783Srmacklem		nlop->nfslo_end = off + len;
966191783Srmacklem		if (nlop->nfslo_end <= nlop->nfslo_first)
967191783Srmacklem			error = NFSERR_INVAL;
968191783Srmacklem	}
969191783Srmacklem
970191783Srmacklem	if (!error) {
971191783Srmacklem		if (recovery)
972191783Srmacklem			clp = rclp;
973191783Srmacklem		else
974244042Srmacklem			error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
975191783Srmacklem	}
976191783Srmacklem	if (error) {
977191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
978191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
979191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
980191783Srmacklem		return (error);
981191783Srmacklem	}
982191783Srmacklem
983191783Srmacklem	op = NULL;
984191783Srmacklem	if (recovery) {
985191783Srmacklem		ownp = rownp;
986223774Srmacklem		openownp = ropenownp;
987191783Srmacklem	} else {
988222719Srmacklem		nfscl_filllockowner(id, own, flags);
989191783Srmacklem		ownp = own;
990223774Srmacklem		nfscl_filllockowner(p->td_proc, openown, F_POSIX);
991223774Srmacklem		openownp = openown;
992191783Srmacklem	}
993191783Srmacklem	if (!recovery) {
994191783Srmacklem		NFSLOCKCLSTATE();
995191783Srmacklem		/*
996191783Srmacklem		 * First, search for a delegation. If one exists for this file,
997191783Srmacklem		 * the lock can be done locally against it, so long as there
998191783Srmacklem		 * isn't a local lock conflict.
999191783Srmacklem		 */
1000191783Srmacklem		ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1001191783Srmacklem		    np->n_fhp->nfh_len);
1002191783Srmacklem		/* Just sanity check for correct type of delegation */
1003214406Srmacklem		if (dp != NULL && ((dp->nfsdl_flags &
1004214406Srmacklem		    (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 ||
1005214406Srmacklem		     (type == F_WRLCK &&
1006214406Srmacklem		      (dp->nfsdl_flags & NFSCLDL_WRITE) == 0)))
1007191783Srmacklem			dp = NULL;
1008191783Srmacklem	}
1009191783Srmacklem	if (dp != NULL) {
1010223774Srmacklem		/* Now, find an open and maybe a lockowner. */
1011191783Srmacklem		ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh,
1012223774Srmacklem		    np->n_fhp->nfh_len, openownp, ownp, mode, NULL, &op);
1013191783Srmacklem		if (ret)
1014191783Srmacklem			ret = nfscl_getopen(&clp->nfsc_owner,
1015223774Srmacklem			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1016223774Srmacklem			    ownp, mode, NULL, &op);
1017191783Srmacklem		if (!ret) {
1018191783Srmacklem			lhp = &dp->nfsdl_lock;
1019191783Srmacklem			TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
1020191783Srmacklem			TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
1021191783Srmacklem			dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
1022191783Srmacklem			donelocally = 1;
1023191783Srmacklem		} else {
1024191783Srmacklem			dp = NULL;
1025191783Srmacklem		}
1026191783Srmacklem	}
1027191783Srmacklem	if (!donelocally) {
1028191783Srmacklem		/*
1029223774Srmacklem		 * Get the related Open and maybe lockowner.
1030191783Srmacklem		 */
1031223774Srmacklem		error = nfscl_getopen(&clp->nfsc_owner,
1032223774Srmacklem		    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1033223774Srmacklem		    ownp, mode, &lp, &op);
1034191783Srmacklem		if (!error)
1035191783Srmacklem			lhp = &op->nfso_lock;
1036191783Srmacklem	}
1037191783Srmacklem	if (!error && !recovery)
1038201439Srmacklem		error = nfscl_localconflict(clp, np->n_fhp->nfh_fh,
1039201439Srmacklem		    np->n_fhp->nfh_len, nlop, ownp, ldp, NULL);
1040191783Srmacklem	if (error) {
1041191783Srmacklem		if (!recovery) {
1042191783Srmacklem			nfscl_clrelease(clp);
1043191783Srmacklem			NFSUNLOCKCLSTATE();
1044191783Srmacklem		}
1045191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1046191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1047191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
1048191783Srmacklem		return (error);
1049191783Srmacklem	}
1050191783Srmacklem
1051191783Srmacklem	/*
1052191783Srmacklem	 * Ok, see if a lockowner exists and create one, as required.
1053191783Srmacklem	 */
1054223774Srmacklem	if (lp == NULL)
1055223774Srmacklem		LIST_FOREACH(lp, lhp, nfsl_list) {
1056223774Srmacklem			if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
1057223774Srmacklem				break;
1058223774Srmacklem		}
1059191783Srmacklem	if (lp == NULL) {
1060191783Srmacklem		NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
1061191783Srmacklem		if (recovery)
1062191783Srmacklem			NFSBCOPY(ropenownp, nlp->nfsl_openowner,
1063191783Srmacklem			    NFSV4CL_LOCKNAMELEN);
1064191783Srmacklem		else
1065191783Srmacklem			NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner,
1066191783Srmacklem			    NFSV4CL_LOCKNAMELEN);
1067191783Srmacklem		nlp->nfsl_seqid = 0;
1068228217Srmacklem		nlp->nfsl_lockflags = flags;
1069191783Srmacklem		nlp->nfsl_inprog = NULL;
1070191783Srmacklem		nfscl_lockinit(&nlp->nfsl_rwlock);
1071191783Srmacklem		LIST_INIT(&nlp->nfsl_lock);
1072191783Srmacklem		if (donelocally) {
1073191783Srmacklem			nlp->nfsl_open = NULL;
1074191783Srmacklem			newnfsstats.cllocallockowners++;
1075191783Srmacklem		} else {
1076191783Srmacklem			nlp->nfsl_open = op;
1077191783Srmacklem			newnfsstats.cllockowners++;
1078191783Srmacklem		}
1079191783Srmacklem		LIST_INSERT_HEAD(lhp, nlp, nfsl_list);
1080191783Srmacklem		lp = nlp;
1081191783Srmacklem		nlp = NULL;
1082191783Srmacklem		*newonep = 1;
1083191783Srmacklem	}
1084191783Srmacklem
1085191783Srmacklem	/*
1086191783Srmacklem	 * Now, update the byte ranges for locks.
1087191783Srmacklem	 */
1088191783Srmacklem	ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally);
1089191783Srmacklem	if (!ret)
1090191783Srmacklem		donelocally = 1;
1091191783Srmacklem	if (donelocally) {
1092191783Srmacklem		*donelocallyp = 1;
1093191783Srmacklem		if (!recovery)
1094191783Srmacklem			nfscl_clrelease(clp);
1095191783Srmacklem	} else {
1096191783Srmacklem		/*
1097191783Srmacklem		 * Serial modifications on the lock owner for multiple threads
1098191783Srmacklem		 * for the same process using a read/write lock.
1099191783Srmacklem		 */
1100191783Srmacklem		if (!recovery)
1101191783Srmacklem			nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1102191783Srmacklem	}
1103191783Srmacklem	if (!recovery)
1104191783Srmacklem		NFSUNLOCKCLSTATE();
1105191783Srmacklem
1106191783Srmacklem	if (nlp)
1107191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1108191783Srmacklem	if (nlop)
1109191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
1110191783Srmacklem	if (otherlop)
1111191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1112191783Srmacklem
1113191783Srmacklem	*lpp = lp;
1114191783Srmacklem	return (0);
1115191783Srmacklem}
1116191783Srmacklem
1117191783Srmacklem/*
1118191783Srmacklem * Called to unlock a byte range, for LockU.
1119191783Srmacklem */
1120191783SrmacklemAPPLESTATIC int
1121191783Srmacklemnfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
1122191783Srmacklem    __unused struct ucred *cred, NFSPROC_T *p, int callcnt,
1123222719Srmacklem    struct nfsclclient *clp, void *id, int flags,
1124222719Srmacklem    struct nfscllockowner **lpp, int *dorpcp)
1125191783Srmacklem{
1126191783Srmacklem	struct nfscllockowner *lp;
1127191783Srmacklem	struct nfsclowner *owp;
1128191783Srmacklem	struct nfsclopen *op;
1129191783Srmacklem	struct nfscllock *nlop, *other_lop = NULL;
1130191783Srmacklem	struct nfscldeleg *dp;
1131191783Srmacklem	struct nfsnode *np;
1132191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1133201439Srmacklem	int ret = 0, fnd;
1134191783Srmacklem
1135191783Srmacklem	np = VTONFS(vp);
1136191783Srmacklem	*lpp = NULL;
1137191783Srmacklem	*dorpcp = 0;
1138191783Srmacklem
1139191783Srmacklem	/*
1140191783Srmacklem	 * Might need these, so MALLOC them now, to
1141191783Srmacklem	 * avoid a tsleep() in MALLOC later.
1142191783Srmacklem	 */
1143191783Srmacklem	MALLOC(nlop, struct nfscllock *,
1144191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1145191783Srmacklem	nlop->nfslo_type = F_UNLCK;
1146191783Srmacklem	nlop->nfslo_first = off;
1147191783Srmacklem	if (len == NFS64BITSSET) {
1148191783Srmacklem		nlop->nfslo_end = NFS64BITSSET;
1149191783Srmacklem	} else {
1150191783Srmacklem		nlop->nfslo_end = off + len;
1151191783Srmacklem		if (nlop->nfslo_end <= nlop->nfslo_first) {
1152191783Srmacklem			FREE((caddr_t)nlop, M_NFSCLLOCK);
1153191783Srmacklem			return (NFSERR_INVAL);
1154191783Srmacklem		}
1155191783Srmacklem	}
1156191783Srmacklem	if (callcnt == 0) {
1157191783Srmacklem		MALLOC(other_lop, struct nfscllock *,
1158191783Srmacklem		    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1159191783Srmacklem		*other_lop = *nlop;
1160191783Srmacklem	}
1161222719Srmacklem	nfscl_filllockowner(id, own, flags);
1162191783Srmacklem	dp = NULL;
1163191783Srmacklem	NFSLOCKCLSTATE();
1164191783Srmacklem	if (callcnt == 0)
1165191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1166191783Srmacklem		    np->n_fhp->nfh_len);
1167191783Srmacklem
1168191783Srmacklem	/*
1169191783Srmacklem	 * First, unlock any local regions on a delegation.
1170191783Srmacklem	 */
1171191783Srmacklem	if (dp != NULL) {
1172191783Srmacklem		/* Look for this lockowner. */
1173191783Srmacklem		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1174191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1175191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1176191783Srmacklem				break;
1177191783Srmacklem		}
1178191783Srmacklem		if (lp != NULL)
1179191783Srmacklem			/* Use other_lop, so nlop is still available */
1180191783Srmacklem			(void)nfscl_updatelock(lp, &other_lop, NULL, 1);
1181191783Srmacklem	}
1182191783Srmacklem
1183191783Srmacklem	/*
1184191783Srmacklem	 * Now, find a matching open/lockowner that hasn't already been done,
1185191783Srmacklem	 * as marked by nfsl_inprog.
1186191783Srmacklem	 */
1187191783Srmacklem	lp = NULL;
1188191783Srmacklem	fnd = 0;
1189191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1190191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1191191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1192191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1193191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1194191783Srmacklem			if (lp->nfsl_inprog == NULL &&
1195191783Srmacklem			    !NFSBCMP(lp->nfsl_owner, own,
1196191783Srmacklem			     NFSV4CL_LOCKNAMELEN)) {
1197191783Srmacklem				fnd = 1;
1198191783Srmacklem				break;
1199191783Srmacklem			}
1200191783Srmacklem		    }
1201191783Srmacklem		    if (fnd)
1202191783Srmacklem			break;
1203191783Srmacklem		}
1204191783Srmacklem	    }
1205191783Srmacklem	    if (fnd)
1206191783Srmacklem		break;
1207191783Srmacklem	}
1208191783Srmacklem
1209191783Srmacklem	if (lp != NULL) {
1210191783Srmacklem		ret = nfscl_updatelock(lp, &nlop, NULL, 0);
1211191783Srmacklem		if (ret)
1212191783Srmacklem			*dorpcp = 1;
1213191783Srmacklem		/*
1214191783Srmacklem		 * Serial modifications on the lock owner for multiple
1215191783Srmacklem		 * threads for the same process using a read/write lock.
1216191783Srmacklem		 */
1217191783Srmacklem		lp->nfsl_inprog = p;
1218191783Srmacklem		nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1219191783Srmacklem		*lpp = lp;
1220191783Srmacklem	}
1221191783Srmacklem	NFSUNLOCKCLSTATE();
1222191783Srmacklem	if (nlop)
1223191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
1224191783Srmacklem	if (other_lop)
1225191783Srmacklem		FREE((caddr_t)other_lop, M_NFSCLLOCK);
1226191783Srmacklem	return (0);
1227191783Srmacklem}
1228191783Srmacklem
1229191783Srmacklem/*
1230191783Srmacklem * Release all lockowners marked in progess for this process and file.
1231191783Srmacklem */
1232191783SrmacklemAPPLESTATIC void
1233222719Srmacklemnfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p,
1234222719Srmacklem    void *id, int flags)
1235191783Srmacklem{
1236191783Srmacklem	struct nfsclowner *owp;
1237191783Srmacklem	struct nfsclopen *op;
1238191783Srmacklem	struct nfscllockowner *lp;
1239191783Srmacklem	struct nfsnode *np;
1240191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1241191783Srmacklem
1242191783Srmacklem	np = VTONFS(vp);
1243222719Srmacklem	nfscl_filllockowner(id, own, flags);
1244191783Srmacklem	NFSLOCKCLSTATE();
1245191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1246191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1247191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1248191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1249191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1250191783Srmacklem			if (lp->nfsl_inprog == p &&
1251191783Srmacklem			    !NFSBCMP(lp->nfsl_owner, own,
1252191783Srmacklem			    NFSV4CL_LOCKNAMELEN)) {
1253191783Srmacklem			    lp->nfsl_inprog = NULL;
1254191783Srmacklem			    nfscl_lockunlock(&lp->nfsl_rwlock);
1255191783Srmacklem			}
1256191783Srmacklem		    }
1257191783Srmacklem		}
1258191783Srmacklem	    }
1259191783Srmacklem	}
1260191783Srmacklem	nfscl_clrelease(clp);
1261191783Srmacklem	NFSUNLOCKCLSTATE();
1262191783Srmacklem}
1263191783Srmacklem
1264191783Srmacklem/*
1265191783Srmacklem * Called to find out if any bytes within the byte range specified are
1266191783Srmacklem * write locked by the calling process. Used to determine if flushing
1267191783Srmacklem * is required before a LockU.
1268191783Srmacklem * If in doubt, return 1, so the flush will occur.
1269191783Srmacklem */
1270191783SrmacklemAPPLESTATIC int
1271191783Srmacklemnfscl_checkwritelocked(vnode_t vp, struct flock *fl,
1272222719Srmacklem    struct ucred *cred, NFSPROC_T *p, void *id, int flags)
1273191783Srmacklem{
1274191783Srmacklem	struct nfsclowner *owp;
1275191783Srmacklem	struct nfscllockowner *lp;
1276191783Srmacklem	struct nfsclopen *op;
1277191783Srmacklem	struct nfsclclient *clp;
1278191783Srmacklem	struct nfscllock *lop;
1279191783Srmacklem	struct nfscldeleg *dp;
1280191783Srmacklem	struct nfsnode *np;
1281191783Srmacklem	u_int64_t off, end;
1282191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1283191783Srmacklem	int error = 0;
1284191783Srmacklem
1285191783Srmacklem	np = VTONFS(vp);
1286191783Srmacklem	switch (fl->l_whence) {
1287191783Srmacklem	case SEEK_SET:
1288191783Srmacklem	case SEEK_CUR:
1289191783Srmacklem		/*
1290191783Srmacklem		 * Caller is responsible for adding any necessary offset
1291191783Srmacklem		 * when SEEK_CUR is used.
1292191783Srmacklem		 */
1293191783Srmacklem		off = fl->l_start;
1294191783Srmacklem		break;
1295191783Srmacklem	case SEEK_END:
1296191783Srmacklem		off = np->n_size + fl->l_start;
1297191783Srmacklem		break;
1298191783Srmacklem	default:
1299191783Srmacklem		return (1);
1300191783Srmacklem	};
1301191783Srmacklem	if (fl->l_len != 0) {
1302191783Srmacklem		end = off + fl->l_len;
1303191783Srmacklem		if (end < off)
1304191783Srmacklem			return (1);
1305191783Srmacklem	} else {
1306191783Srmacklem		end = NFS64BITSSET;
1307191783Srmacklem	}
1308191783Srmacklem
1309244042Srmacklem	error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
1310191783Srmacklem	if (error)
1311191783Srmacklem		return (1);
1312222719Srmacklem	nfscl_filllockowner(id, own, flags);
1313191783Srmacklem	NFSLOCKCLSTATE();
1314191783Srmacklem
1315191783Srmacklem	/*
1316191783Srmacklem	 * First check the delegation locks.
1317191783Srmacklem	 */
1318191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
1319191783Srmacklem	if (dp != NULL) {
1320191783Srmacklem		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1321191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1322191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1323191783Srmacklem				break;
1324191783Srmacklem		}
1325191783Srmacklem		if (lp != NULL) {
1326191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1327191783Srmacklem				if (lop->nfslo_first >= end)
1328191783Srmacklem					break;
1329191783Srmacklem				if (lop->nfslo_end <= off)
1330191783Srmacklem					continue;
1331191783Srmacklem				if (lop->nfslo_type == F_WRLCK) {
1332191783Srmacklem					nfscl_clrelease(clp);
1333191783Srmacklem					NFSUNLOCKCLSTATE();
1334191783Srmacklem					return (1);
1335191783Srmacklem				}
1336191783Srmacklem			}
1337191783Srmacklem		}
1338191783Srmacklem	}
1339191783Srmacklem
1340191783Srmacklem	/*
1341191783Srmacklem	 * Now, check state against the server.
1342191783Srmacklem	 */
1343191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1344191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1345191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1346191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1347191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1348191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1349191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1350191783Srmacklem			    break;
1351191783Srmacklem		    }
1352191783Srmacklem		    if (lp != NULL) {
1353191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1354191783Srmacklem			    if (lop->nfslo_first >= end)
1355191783Srmacklem				break;
1356191783Srmacklem			    if (lop->nfslo_end <= off)
1357191783Srmacklem				continue;
1358191783Srmacklem			    if (lop->nfslo_type == F_WRLCK) {
1359191783Srmacklem				nfscl_clrelease(clp);
1360191783Srmacklem				NFSUNLOCKCLSTATE();
1361191783Srmacklem				return (1);
1362191783Srmacklem			    }
1363191783Srmacklem			}
1364191783Srmacklem		    }
1365191783Srmacklem		}
1366191783Srmacklem	    }
1367191783Srmacklem	}
1368191783Srmacklem	nfscl_clrelease(clp);
1369191783Srmacklem	NFSUNLOCKCLSTATE();
1370191783Srmacklem	return (0);
1371191783Srmacklem}
1372191783Srmacklem
1373191783Srmacklem/*
1374191783Srmacklem * Release a byte range lock owner structure.
1375191783Srmacklem */
1376191783SrmacklemAPPLESTATIC void
1377191783Srmacklemnfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete)
1378191783Srmacklem{
1379191783Srmacklem	struct nfsclclient *clp;
1380191783Srmacklem
1381191783Srmacklem	if (lp == NULL)
1382191783Srmacklem		return;
1383191783Srmacklem	NFSLOCKCLSTATE();
1384191783Srmacklem	clp = lp->nfsl_open->nfso_own->nfsow_clp;
1385191783Srmacklem	if (error != 0 && candelete &&
1386191783Srmacklem	    (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0)
1387191783Srmacklem		nfscl_freelockowner(lp, 0);
1388191783Srmacklem	else
1389191783Srmacklem		nfscl_lockunlock(&lp->nfsl_rwlock);
1390191783Srmacklem	nfscl_clrelease(clp);
1391191783Srmacklem	NFSUNLOCKCLSTATE();
1392191783Srmacklem}
1393191783Srmacklem
1394191783Srmacklem/*
1395191783Srmacklem * Free up an open structure and any associated byte range lock structures.
1396191783Srmacklem */
1397191783SrmacklemAPPLESTATIC void
1398191783Srmacklemnfscl_freeopen(struct nfsclopen *op, int local)
1399191783Srmacklem{
1400191783Srmacklem
1401191783Srmacklem	LIST_REMOVE(op, nfso_list);
1402191783Srmacklem	nfscl_freealllocks(&op->nfso_lock, local);
1403191783Srmacklem	FREE((caddr_t)op, M_NFSCLOPEN);
1404191783Srmacklem	if (local)
1405191783Srmacklem		newnfsstats.cllocalopens--;
1406191783Srmacklem	else
1407191783Srmacklem		newnfsstats.clopens--;
1408191783Srmacklem}
1409191783Srmacklem
1410191783Srmacklem/*
1411191783Srmacklem * Free up all lock owners and associated locks.
1412191783Srmacklem */
1413191783Srmacklemstatic void
1414191783Srmacklemnfscl_freealllocks(struct nfscllockownerhead *lhp, int local)
1415191783Srmacklem{
1416191783Srmacklem	struct nfscllockowner *lp, *nlp;
1417191783Srmacklem
1418191783Srmacklem	LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) {
1419191783Srmacklem		if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1420191783Srmacklem			panic("nfscllckw");
1421191783Srmacklem		nfscl_freelockowner(lp, local);
1422191783Srmacklem	}
1423191783Srmacklem}
1424191783Srmacklem
1425191783Srmacklem/*
1426191783Srmacklem * Called for an Open when NFSERR_EXPIRED is received from the server.
1427191783Srmacklem * If there are no byte range locks nor a Share Deny lost, try to do a
1428191783Srmacklem * fresh Open. Otherwise, free the open.
1429191783Srmacklem */
1430191783Srmacklemstatic int
1431191783Srmacklemnfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op,
1432191783Srmacklem    struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
1433191783Srmacklem{
1434191783Srmacklem	struct nfscllockowner *lp;
1435191783Srmacklem	struct nfscldeleg *dp;
1436191783Srmacklem	int mustdelete = 0, error;
1437191783Srmacklem
1438191783Srmacklem	/*
1439191783Srmacklem	 * Look for any byte range lock(s).
1440191783Srmacklem	 */
1441191783Srmacklem	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1442191783Srmacklem		if (!LIST_EMPTY(&lp->nfsl_lock)) {
1443191783Srmacklem			mustdelete = 1;
1444191783Srmacklem			break;
1445191783Srmacklem		}
1446191783Srmacklem	}
1447191783Srmacklem
1448191783Srmacklem	/*
1449191783Srmacklem	 * If no byte range lock(s) nor a Share deny, try to re-open.
1450191783Srmacklem	 */
1451191783Srmacklem	if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) {
1452191783Srmacklem		newnfs_copycred(&op->nfso_cred, cred);
1453191783Srmacklem		dp = NULL;
1454191783Srmacklem		error = nfsrpc_reopen(nmp, op->nfso_fh,
1455191783Srmacklem		    op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p);
1456191783Srmacklem		if (error) {
1457191783Srmacklem			mustdelete = 1;
1458191783Srmacklem			if (dp != NULL) {
1459191783Srmacklem				FREE((caddr_t)dp, M_NFSCLDELEG);
1460191783Srmacklem				dp = NULL;
1461191783Srmacklem			}
1462191783Srmacklem		}
1463191783Srmacklem		if (dp != NULL)
1464191783Srmacklem			nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh,
1465191783Srmacklem			    op->nfso_fhlen, cred, p, &dp);
1466191783Srmacklem	}
1467191783Srmacklem
1468191783Srmacklem	/*
1469191783Srmacklem	 * If a byte range lock or Share deny or couldn't re-open, free it.
1470191783Srmacklem	 */
1471191783Srmacklem	if (mustdelete)
1472191783Srmacklem		nfscl_freeopen(op, 0);
1473191783Srmacklem	return (mustdelete);
1474191783Srmacklem}
1475191783Srmacklem
1476191783Srmacklem/*
1477191783Srmacklem * Free up an open owner structure.
1478191783Srmacklem */
1479191783Srmacklemstatic void
1480191783Srmacklemnfscl_freeopenowner(struct nfsclowner *owp, int local)
1481191783Srmacklem{
1482191783Srmacklem
1483191783Srmacklem	LIST_REMOVE(owp, nfsow_list);
1484191783Srmacklem	FREE((caddr_t)owp, M_NFSCLOWNER);
1485191783Srmacklem	if (local)
1486191783Srmacklem		newnfsstats.cllocalopenowners--;
1487191783Srmacklem	else
1488191783Srmacklem		newnfsstats.clopenowners--;
1489191783Srmacklem}
1490191783Srmacklem
1491191783Srmacklem/*
1492191783Srmacklem * Free up a byte range lock owner structure.
1493191783Srmacklem */
1494223747SrmacklemAPPLESTATIC void
1495191783Srmacklemnfscl_freelockowner(struct nfscllockowner *lp, int local)
1496191783Srmacklem{
1497191783Srmacklem	struct nfscllock *lop, *nlop;
1498191783Srmacklem
1499191783Srmacklem	LIST_REMOVE(lp, nfsl_list);
1500191783Srmacklem	LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
1501191783Srmacklem		nfscl_freelock(lop, local);
1502191783Srmacklem	}
1503191783Srmacklem	FREE((caddr_t)lp, M_NFSCLLOCKOWNER);
1504191783Srmacklem	if (local)
1505191783Srmacklem		newnfsstats.cllocallockowners--;
1506191783Srmacklem	else
1507191783Srmacklem		newnfsstats.cllockowners--;
1508191783Srmacklem}
1509191783Srmacklem
1510191783Srmacklem/*
1511191783Srmacklem * Free up a byte range lock structure.
1512191783Srmacklem */
1513191783SrmacklemAPPLESTATIC void
1514191783Srmacklemnfscl_freelock(struct nfscllock *lop, int local)
1515191783Srmacklem{
1516191783Srmacklem
1517191783Srmacklem	LIST_REMOVE(lop, nfslo_list);
1518191783Srmacklem	FREE((caddr_t)lop, M_NFSCLLOCK);
1519191783Srmacklem	if (local)
1520191783Srmacklem		newnfsstats.cllocallocks--;
1521191783Srmacklem	else
1522191783Srmacklem		newnfsstats.cllocks--;
1523191783Srmacklem}
1524191783Srmacklem
1525191783Srmacklem/*
1526191783Srmacklem * Clean out the state related to a delegation.
1527191783Srmacklem */
1528191783Srmacklemstatic void
1529191783Srmacklemnfscl_cleandeleg(struct nfscldeleg *dp)
1530191783Srmacklem{
1531191783Srmacklem	struct nfsclowner *owp, *nowp;
1532191783Srmacklem	struct nfsclopen *op;
1533191783Srmacklem
1534191783Srmacklem	LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
1535191783Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
1536191783Srmacklem		if (op != NULL) {
1537191783Srmacklem			if (LIST_NEXT(op, nfso_list) != NULL)
1538191783Srmacklem				panic("nfscleandel");
1539191783Srmacklem			nfscl_freeopen(op, 1);
1540191783Srmacklem		}
1541191783Srmacklem		nfscl_freeopenowner(owp, 1);
1542191783Srmacklem	}
1543191783Srmacklem	nfscl_freealllocks(&dp->nfsdl_lock, 1);
1544191783Srmacklem}
1545191783Srmacklem
1546191783Srmacklem/*
1547191783Srmacklem * Free a delegation.
1548191783Srmacklem */
1549191783Srmacklemstatic void
1550191783Srmacklemnfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp)
1551191783Srmacklem{
1552191783Srmacklem
1553191783Srmacklem	TAILQ_REMOVE(hdp, dp, nfsdl_list);
1554191783Srmacklem	LIST_REMOVE(dp, nfsdl_hash);
1555191783Srmacklem	FREE((caddr_t)dp, M_NFSCLDELEG);
1556191783Srmacklem	newnfsstats.cldelegates--;
1557191783Srmacklem	nfscl_delegcnt--;
1558191783Srmacklem}
1559191783Srmacklem
1560191783Srmacklem/*
1561191783Srmacklem * Free up all state related to this client structure.
1562191783Srmacklem */
1563191783Srmacklemstatic void
1564191783Srmacklemnfscl_cleanclient(struct nfsclclient *clp)
1565191783Srmacklem{
1566191783Srmacklem	struct nfsclowner *owp, *nowp;
1567191783Srmacklem	struct nfsclopen *op, *nop;
1568191783Srmacklem
1569191783Srmacklem	/* Now, all the OpenOwners, etc. */
1570191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1571191783Srmacklem		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1572191783Srmacklem			nfscl_freeopen(op, 0);
1573191783Srmacklem		}
1574191783Srmacklem		nfscl_freeopenowner(owp, 0);
1575191783Srmacklem	}
1576191783Srmacklem}
1577191783Srmacklem
1578191783Srmacklem/*
1579191783Srmacklem * Called when an NFSERR_EXPIRED is received from the server.
1580191783Srmacklem */
1581191783Srmacklemstatic void
1582191783Srmacklemnfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
1583191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
1584191783Srmacklem{
1585191783Srmacklem	struct nfsclowner *owp, *nowp, *towp;
1586191783Srmacklem	struct nfsclopen *op, *nop, *top;
1587191783Srmacklem	struct nfscldeleg *dp, *ndp;
1588191783Srmacklem	int ret, printed = 0;
1589191783Srmacklem
1590191783Srmacklem	/*
1591191783Srmacklem	 * First, merge locally issued Opens into the list for the server.
1592191783Srmacklem	 */
1593191783Srmacklem	dp = TAILQ_FIRST(&clp->nfsc_deleg);
1594191783Srmacklem	while (dp != NULL) {
1595191783Srmacklem	    ndp = TAILQ_NEXT(dp, nfsdl_list);
1596191783Srmacklem	    owp = LIST_FIRST(&dp->nfsdl_owner);
1597191783Srmacklem	    while (owp != NULL) {
1598191783Srmacklem		nowp = LIST_NEXT(owp, nfsow_list);
1599191783Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
1600191783Srmacklem		if (op != NULL) {
1601191783Srmacklem		    if (LIST_NEXT(op, nfso_list) != NULL)
1602191783Srmacklem			panic("nfsclexp");
1603191783Srmacklem		    LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) {
1604191783Srmacklem			if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner,
1605191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1606191783Srmacklem			    break;
1607191783Srmacklem		    }
1608191783Srmacklem		    if (towp != NULL) {
1609191783Srmacklem			/* Merge opens in */
1610191783Srmacklem			LIST_FOREACH(top, &towp->nfsow_open, nfso_list) {
1611191783Srmacklem			    if (top->nfso_fhlen == op->nfso_fhlen &&
1612191783Srmacklem				!NFSBCMP(top->nfso_fh, op->nfso_fh,
1613191783Srmacklem				 op->nfso_fhlen)) {
1614191783Srmacklem				top->nfso_mode |= op->nfso_mode;
1615191783Srmacklem				top->nfso_opencnt += op->nfso_opencnt;
1616191783Srmacklem				break;
1617191783Srmacklem			    }
1618191783Srmacklem			}
1619191783Srmacklem			if (top == NULL) {
1620191783Srmacklem			    /* Just add the open to the owner list */
1621191783Srmacklem			    LIST_REMOVE(op, nfso_list);
1622191783Srmacklem			    op->nfso_own = towp;
1623191783Srmacklem			    LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list);
1624191783Srmacklem			    newnfsstats.cllocalopens--;
1625191783Srmacklem			    newnfsstats.clopens++;
1626191783Srmacklem			}
1627191783Srmacklem		    } else {
1628191783Srmacklem			/* Just add the openowner to the client list */
1629191783Srmacklem			LIST_REMOVE(owp, nfsow_list);
1630191783Srmacklem			owp->nfsow_clp = clp;
1631191783Srmacklem			LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list);
1632191783Srmacklem			newnfsstats.cllocalopenowners--;
1633191783Srmacklem			newnfsstats.clopenowners++;
1634191783Srmacklem			newnfsstats.cllocalopens--;
1635191783Srmacklem			newnfsstats.clopens++;
1636191783Srmacklem		    }
1637191783Srmacklem		}
1638191783Srmacklem		owp = nowp;
1639191783Srmacklem	    }
1640191783Srmacklem	    if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) {
1641191783Srmacklem		printed = 1;
1642191783Srmacklem		printf("nfsv4 expired locks lost\n");
1643191783Srmacklem	    }
1644191783Srmacklem	    nfscl_cleandeleg(dp);
1645191783Srmacklem	    nfscl_freedeleg(&clp->nfsc_deleg, dp);
1646191783Srmacklem	    dp = ndp;
1647191783Srmacklem	}
1648191783Srmacklem	if (!TAILQ_EMPTY(&clp->nfsc_deleg))
1649191783Srmacklem	    panic("nfsclexp");
1650191783Srmacklem
1651191783Srmacklem	/*
1652191783Srmacklem	 * Now, try and reopen against the server.
1653191783Srmacklem	 */
1654191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1655191783Srmacklem		owp->nfsow_seqid = 0;
1656191783Srmacklem		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1657191783Srmacklem			ret = nfscl_expireopen(clp, op, nmp, cred, p);
1658191783Srmacklem			if (ret && !printed) {
1659191783Srmacklem				printed = 1;
1660191783Srmacklem				printf("nfsv4 expired locks lost\n");
1661191783Srmacklem			}
1662191783Srmacklem		}
1663191783Srmacklem		if (LIST_EMPTY(&owp->nfsow_open))
1664191783Srmacklem			nfscl_freeopenowner(owp, 0);
1665191783Srmacklem	}
1666191783Srmacklem}
1667191783Srmacklem
1668191783Srmacklem/*
1669227744Srmacklem * This function must be called after the process represented by "own" has
1670227744Srmacklem * exited. Must be called with CLSTATE lock held.
1671191783Srmacklem */
1672191783Srmacklemstatic void
1673191783Srmacklemnfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own)
1674191783Srmacklem{
1675191783Srmacklem	struct nfsclowner *owp, *nowp;
1676191783Srmacklem	struct nfscllockowner *lp, *nlp;
1677191783Srmacklem	struct nfscldeleg *dp;
1678191783Srmacklem
1679191783Srmacklem	/* First, get rid of local locks on delegations. */
1680191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1681191783Srmacklem		LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) {
1682191783Srmacklem		    if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
1683191783Srmacklem			if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1684191783Srmacklem			    panic("nfscllckw");
1685191783Srmacklem			nfscl_freelockowner(lp, 1);
1686191783Srmacklem		    }
1687191783Srmacklem		}
1688191783Srmacklem	}
1689191783Srmacklem	owp = LIST_FIRST(&clp->nfsc_owner);
1690191783Srmacklem	while (owp != NULL) {
1691191783Srmacklem		nowp = LIST_NEXT(owp, nfsow_list);
1692191783Srmacklem		if (!NFSBCMP(owp->nfsow_owner, own,
1693191783Srmacklem		    NFSV4CL_LOCKNAMELEN)) {
1694191783Srmacklem			/*
1695191783Srmacklem			 * If there are children that haven't closed the
1696191783Srmacklem			 * file descriptors yet, the opens will still be
1697191783Srmacklem			 * here. For that case, let the renew thread clear
1698191783Srmacklem			 * out the OpenOwner later.
1699191783Srmacklem			 */
1700191783Srmacklem			if (LIST_EMPTY(&owp->nfsow_open))
1701191783Srmacklem				nfscl_freeopenowner(owp, 0);
1702191783Srmacklem			else
1703191783Srmacklem				owp->nfsow_defunct = 1;
1704191783Srmacklem		}
1705191783Srmacklem		owp = nowp;
1706191783Srmacklem	}
1707191783Srmacklem}
1708191783Srmacklem
1709191783Srmacklem/*
1710227744Srmacklem * Find open/lock owners for processes that have exited.
1711191783Srmacklem */
1712191783Srmacklemstatic void
1713228217Srmacklemnfscl_cleanupkext(struct nfsclclient *clp, struct nfscllockownerfhhead *lhp)
1714191783Srmacklem{
1715191783Srmacklem	struct nfsclowner *owp, *nowp;
1716228217Srmacklem	struct nfsclopen *op;
1717228217Srmacklem	struct nfscllockowner *lp, *nlp;
1718191783Srmacklem
1719191783Srmacklem	NFSPROCLISTLOCK();
1720191783Srmacklem	NFSLOCKCLSTATE();
1721191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1722228217Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1723228217Srmacklem			LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp) {
1724228217Srmacklem				if (LIST_EMPTY(&lp->nfsl_lock))
1725228217Srmacklem					nfscl_emptylockowner(lp, lhp);
1726228217Srmacklem			}
1727228217Srmacklem		}
1728191783Srmacklem		if (nfscl_procdoesntexist(owp->nfsow_owner))
1729191783Srmacklem			nfscl_cleanup_common(clp, owp->nfsow_owner);
1730191783Srmacklem	}
1731191783Srmacklem	NFSUNLOCKCLSTATE();
1732191783Srmacklem	NFSPROCLISTUNLOCK();
1733191783Srmacklem}
1734191783Srmacklem
1735228217Srmacklem/*
1736228217Srmacklem * Take the empty lock owner and move it to the local lhp list if the
1737228217Srmacklem * associated process no longer exists.
1738228217Srmacklem */
1739228217Srmacklemstatic void
1740228217Srmacklemnfscl_emptylockowner(struct nfscllockowner *lp,
1741228217Srmacklem    struct nfscllockownerfhhead *lhp)
1742228217Srmacklem{
1743228217Srmacklem	struct nfscllockownerfh *lfhp, *mylfhp;
1744228217Srmacklem	struct nfscllockowner *nlp;
1745228217Srmacklem	int fnd_it;
1746228217Srmacklem
1747228217Srmacklem	/* If not a Posix lock owner, just return. */
1748228217Srmacklem	if ((lp->nfsl_lockflags & F_POSIX) == 0)
1749228217Srmacklem		return;
1750228217Srmacklem
1751228217Srmacklem	fnd_it = 0;
1752228217Srmacklem	mylfhp = NULL;
1753228217Srmacklem	/*
1754228217Srmacklem	 * First, search to see if this lock owner is already in the list.
1755228217Srmacklem	 * If it is, then the associated process no longer exists.
1756228217Srmacklem	 */
1757228217Srmacklem	SLIST_FOREACH(lfhp, lhp, nfslfh_list) {
1758228217Srmacklem		if (lfhp->nfslfh_len == lp->nfsl_open->nfso_fhlen &&
1759228217Srmacklem		    !NFSBCMP(lfhp->nfslfh_fh, lp->nfsl_open->nfso_fh,
1760228217Srmacklem		    lfhp->nfslfh_len))
1761228217Srmacklem			mylfhp = lfhp;
1762228217Srmacklem		LIST_FOREACH(nlp, &lfhp->nfslfh_lock, nfsl_list)
1763228217Srmacklem			if (!NFSBCMP(nlp->nfsl_owner, lp->nfsl_owner,
1764228217Srmacklem			    NFSV4CL_LOCKNAMELEN))
1765228217Srmacklem				fnd_it = 1;
1766228217Srmacklem	}
1767228217Srmacklem	/* If not found, check if process still exists. */
1768228217Srmacklem	if (fnd_it == 0 && nfscl_procdoesntexist(lp->nfsl_owner) == 0)
1769228217Srmacklem		return;
1770228217Srmacklem
1771228217Srmacklem	/* Move the lock owner over to the local list. */
1772228217Srmacklem	if (mylfhp == NULL) {
1773228217Srmacklem		mylfhp = malloc(sizeof(struct nfscllockownerfh), M_TEMP,
1774228217Srmacklem		    M_NOWAIT);
1775228217Srmacklem		if (mylfhp == NULL)
1776228217Srmacklem			return;
1777228217Srmacklem		mylfhp->nfslfh_len = lp->nfsl_open->nfso_fhlen;
1778228217Srmacklem		NFSBCOPY(lp->nfsl_open->nfso_fh, mylfhp->nfslfh_fh,
1779228217Srmacklem		    mylfhp->nfslfh_len);
1780228217Srmacklem		LIST_INIT(&mylfhp->nfslfh_lock);
1781228217Srmacklem		SLIST_INSERT_HEAD(lhp, mylfhp, nfslfh_list);
1782228217Srmacklem	}
1783228217Srmacklem	LIST_REMOVE(lp, nfsl_list);
1784228217Srmacklem	LIST_INSERT_HEAD(&mylfhp->nfslfh_lock, lp, nfsl_list);
1785228217Srmacklem}
1786228217Srmacklem
1787222389Srmacklemstatic int	fake_global;	/* Used to force visibility of MNTK_UNMOUNTF */
1788191783Srmacklem/*
1789191783Srmacklem * Called from nfs umount to free up the clientid.
1790191783Srmacklem */
1791191783SrmacklemAPPLESTATIC void
1792191783Srmacklemnfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
1793191783Srmacklem{
1794191783Srmacklem	struct nfsclclient *clp;
1795191783Srmacklem	struct ucred *cred;
1796191783Srmacklem	int igotlock;
1797191783Srmacklem
1798222389Srmacklem	/*
1799222389Srmacklem	 * For the case that matters, this is the thread that set
1800222389Srmacklem	 * MNTK_UNMOUNTF, so it will see it set. The code that follows is
1801222389Srmacklem	 * done to ensure that any thread executing nfscl_getcl() after
1802222389Srmacklem	 * this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the
1803222389Srmacklem	 * mutex for NFSLOCKCLSTATE(), so it is "m" for the following
1804222389Srmacklem	 * explanation, courtesy of Alan Cox.
1805222389Srmacklem	 * What follows is a snippet from Alan Cox's email at:
1806222389Srmacklem	 * http://docs.FreeBSD.org/cgi/
1807222389Srmacklem	 *     mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw
1808222389Srmacklem	 *
1809222389Srmacklem	 * 1. Set MNTK_UNMOUNTF
1810222389Srmacklem	 * 2. Acquire a standard FreeBSD mutex "m".
1811222389Srmacklem	 * 3. Update some data structures.
1812222389Srmacklem	 * 4. Release mutex "m".
1813222389Srmacklem	 *
1814222389Srmacklem	 * Then, other threads that acquire "m" after step 4 has occurred will
1815222389Srmacklem	 * see MNTK_UNMOUNTF as set.  But, other threads that beat thread X to
1816222389Srmacklem	 * step 2 may or may not see MNTK_UNMOUNTF as set.
1817222389Srmacklem	 */
1818222389Srmacklem	NFSLOCKCLSTATE();
1819222389Srmacklem	if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1820222389Srmacklem		fake_global++;
1821222389Srmacklem		NFSUNLOCKCLSTATE();
1822222389Srmacklem		NFSLOCKCLSTATE();
1823222389Srmacklem	}
1824222389Srmacklem
1825191783Srmacklem	clp = nmp->nm_clp;
1826191783Srmacklem	if (clp != NULL) {
1827191783Srmacklem		if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
1828191783Srmacklem			panic("nfscl umount");
1829191783Srmacklem
1830191783Srmacklem		/*
1831191783Srmacklem		 * First, handshake with the nfscl renew thread, to terminate
1832191783Srmacklem		 * it.
1833191783Srmacklem		 */
1834191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
1835191783Srmacklem		while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
1836222389Srmacklem			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT,
1837222389Srmacklem			    "nfsclumnt", hz);
1838191783Srmacklem
1839222389Srmacklem		/*
1840222389Srmacklem		 * Now, get the exclusive lock on the client state, so
1841222389Srmacklem		 * that no uses of the state are still in progress.
1842222389Srmacklem		 */
1843191783Srmacklem		do {
1844191783Srmacklem			igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1845222389Srmacklem			    NFSCLSTATEMUTEXPTR, NULL);
1846191783Srmacklem		} while (!igotlock);
1847191783Srmacklem		NFSUNLOCKCLSTATE();
1848191783Srmacklem
1849191783Srmacklem		/*
1850191783Srmacklem		 * Free up all the state. It will expire on the server, but
1851191783Srmacklem		 * maybe we should do a SetClientId/SetClientIdConfirm so
1852191783Srmacklem		 * the server throws it away?
1853191783Srmacklem		 */
1854191783Srmacklem		LIST_REMOVE(clp, nfsc_list);
1855191783Srmacklem		nfscl_delegreturnall(clp, p);
1856191783Srmacklem		cred = newnfs_getcred();
1857244042Srmacklem		if (NFSHASNFSV4N(nmp)) {
1858244042Srmacklem			(void)nfsrpc_destroysession(nmp, clp, cred, p);
1859244042Srmacklem			(void)nfsrpc_destroyclient(nmp, clp, cred, p);
1860244042Srmacklem		} else
1861244042Srmacklem			(void)nfsrpc_setclient(nmp, clp, 0, cred, p);
1862191783Srmacklem		nfscl_cleanclient(clp);
1863191783Srmacklem		nmp->nm_clp = NULL;
1864191783Srmacklem		NFSFREECRED(cred);
1865244042Srmacklem		free(clp, M_NFSCLCLIENT);
1866222389Srmacklem	} else
1867222389Srmacklem		NFSUNLOCKCLSTATE();
1868191783Srmacklem}
1869191783Srmacklem
1870191783Srmacklem/*
1871191783Srmacklem * This function is called when a server replies with NFSERR_STALECLIENTID
1872244042Srmacklem * NFSERR_STALESTATEID or NFSERR_BADSESSION. It traverses the clientid lists,
1873244042Srmacklem * doing Opens and Locks with reclaim. If these fail, it deletes the
1874244042Srmacklem * corresponding state.
1875191783Srmacklem */
1876191783Srmacklemstatic void
1877191783Srmacklemnfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
1878191783Srmacklem{
1879191783Srmacklem	struct nfsclowner *owp, *nowp;
1880191783Srmacklem	struct nfsclopen *op, *nop;
1881191783Srmacklem	struct nfscllockowner *lp, *nlp;
1882191783Srmacklem	struct nfscllock *lop, *nlop;
1883191783Srmacklem	struct nfscldeleg *dp, *ndp, *tdp;
1884191783Srmacklem	struct nfsmount *nmp;
1885191783Srmacklem	struct ucred *tcred;
1886191783Srmacklem	struct nfsclopenhead extra_open;
1887191783Srmacklem	struct nfscldeleghead extra_deleg;
1888191783Srmacklem	struct nfsreq *rep;
1889191783Srmacklem	u_int64_t len;
1890191783Srmacklem	u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
1891247072Simp	int i, igotlock = 0, error, trycnt, firstlock;
1892244042Srmacklem	struct nfscllayout *lyp, *nlyp;
1893191783Srmacklem
1894191783Srmacklem	/*
1895191783Srmacklem	 * First, lock the client structure, so everyone else will
1896191783Srmacklem	 * block when trying to use state.
1897191783Srmacklem	 */
1898191783Srmacklem	NFSLOCKCLSTATE();
1899206818Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
1900191783Srmacklem	do {
1901191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1902222389Srmacklem		    NFSCLSTATEMUTEXPTR, NULL);
1903191783Srmacklem	} while (!igotlock);
1904191783Srmacklem	NFSUNLOCKCLSTATE();
1905191783Srmacklem
1906191783Srmacklem	nmp = clp->nfsc_nmp;
1907191783Srmacklem	if (nmp == NULL)
1908191783Srmacklem		panic("nfscl recover");
1909244042Srmacklem
1910244042Srmacklem	/*
1911244042Srmacklem	 * For now, just get rid of all layouts. There may be a need
1912244042Srmacklem	 * to do LayoutCommit Ops with reclaim == true later.
1913244042Srmacklem	 */
1914244042Srmacklem	TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp)
1915244042Srmacklem		nfscl_freelayout(lyp);
1916244042Srmacklem	TAILQ_INIT(&clp->nfsc_layout);
1917244042Srmacklem	for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++)
1918244042Srmacklem		LIST_INIT(&clp->nfsc_layouthash[i]);
1919244042Srmacklem
1920191783Srmacklem	trycnt = 5;
1921191783Srmacklem	do {
1922244042Srmacklem		error = nfsrpc_setclient(nmp, clp, 1, cred, p);
1923191783Srmacklem	} while ((error == NFSERR_STALECLIENTID ||
1924244042Srmacklem	     error == NFSERR_BADSESSION ||
1925191783Srmacklem	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
1926191783Srmacklem	if (error) {
1927191783Srmacklem		nfscl_cleanclient(clp);
1928206818Srmacklem		NFSLOCKCLSTATE();
1929191783Srmacklem		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
1930206818Srmacklem		    NFSCLFLAGS_RECOVER | NFSCLFLAGS_RECVRINPROG);
1931206818Srmacklem		wakeup(&clp->nfsc_flags);
1932191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
1933191783Srmacklem		NFSUNLOCKCLSTATE();
1934191783Srmacklem		return;
1935191783Srmacklem	}
1936191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
1937191783Srmacklem	clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
1938191783Srmacklem
1939191783Srmacklem	/*
1940191783Srmacklem	 * Mark requests already queued on the server, so that they don't
1941191783Srmacklem	 * initiate another recovery cycle. Any requests already in the
1942191783Srmacklem	 * queue that handle state information will have the old stale
1943244042Srmacklem	 * clientid/stateid and will get a NFSERR_STALESTATEID,
1944244042Srmacklem	 * NFSERR_STALECLIENTID or NFSERR_BADSESSION reply from the server.
1945244042Srmacklem	 * This will be translated to NFSERR_STALEDONTRECOVER when
1946244042Srmacklem	 * R_DONTRECOVER is set.
1947191783Srmacklem	 */
1948191783Srmacklem	NFSLOCKREQ();
1949191783Srmacklem	TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) {
1950191783Srmacklem		if (rep->r_nmp == nmp)
1951191783Srmacklem			rep->r_flags |= R_DONTRECOVER;
1952191783Srmacklem	}
1953191783Srmacklem	NFSUNLOCKREQ();
1954191783Srmacklem
1955191783Srmacklem	/*
1956191783Srmacklem	 * Now, mark all delegations "need reclaim".
1957191783Srmacklem	 */
1958191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
1959191783Srmacklem		dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM;
1960191783Srmacklem
1961191783Srmacklem	TAILQ_INIT(&extra_deleg);
1962191783Srmacklem	LIST_INIT(&extra_open);
1963191783Srmacklem	/*
1964191783Srmacklem	 * Now traverse the state lists, doing Open and Lock Reclaims.
1965191783Srmacklem	 */
1966191783Srmacklem	tcred = newnfs_getcred();
1967191783Srmacklem	owp = LIST_FIRST(&clp->nfsc_owner);
1968191783Srmacklem	while (owp != NULL) {
1969191783Srmacklem	    nowp = LIST_NEXT(owp, nfsow_list);
1970191783Srmacklem	    owp->nfsow_seqid = 0;
1971191783Srmacklem	    op = LIST_FIRST(&owp->nfsow_open);
1972191783Srmacklem	    while (op != NULL) {
1973191783Srmacklem		nop = LIST_NEXT(op, nfso_list);
1974191783Srmacklem		if (error != NFSERR_NOGRACE) {
1975191783Srmacklem		    /* Search for a delegation to reclaim with the open */
1976191783Srmacklem		    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1977191783Srmacklem			if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1978191783Srmacklem			    continue;
1979191783Srmacklem			if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
1980191783Srmacklem			    mode = NFSV4OPEN_ACCESSWRITE;
1981191783Srmacklem			    delegtype = NFSV4OPEN_DELEGATEWRITE;
1982191783Srmacklem			} else {
1983191783Srmacklem			    mode = NFSV4OPEN_ACCESSREAD;
1984191783Srmacklem			    delegtype = NFSV4OPEN_DELEGATEREAD;
1985191783Srmacklem			}
1986191783Srmacklem			if ((op->nfso_mode & mode) == mode &&
1987191783Srmacklem			    op->nfso_fhlen == dp->nfsdl_fhlen &&
1988191783Srmacklem			    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen))
1989191783Srmacklem			    break;
1990191783Srmacklem		    }
1991191783Srmacklem		    ndp = dp;
1992191783Srmacklem		    if (dp == NULL)
1993191783Srmacklem			delegtype = NFSV4OPEN_DELEGATENONE;
1994191783Srmacklem		    newnfs_copycred(&op->nfso_cred, tcred);
1995191783Srmacklem		    error = nfscl_tryopen(nmp, NULL, op->nfso_fh,
1996191783Srmacklem			op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen,
1997191783Srmacklem			op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
1998191783Srmacklem			tcred, p);
1999191783Srmacklem		    if (!error) {
2000191783Srmacklem			/* Handle any replied delegation */
2001191783Srmacklem			if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
2002191783Srmacklem			    || NFSMNT_RDONLY(nmp->nm_mountp))) {
2003191783Srmacklem			    if ((ndp->nfsdl_flags & NFSCLDL_WRITE))
2004191783Srmacklem				mode = NFSV4OPEN_ACCESSWRITE;
2005191783Srmacklem			    else
2006191783Srmacklem				mode = NFSV4OPEN_ACCESSREAD;
2007191783Srmacklem			    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2008191783Srmacklem				if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
2009191783Srmacklem				    continue;
2010191783Srmacklem				if ((op->nfso_mode & mode) == mode &&
2011191783Srmacklem				    op->nfso_fhlen == dp->nfsdl_fhlen &&
2012191783Srmacklem				    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh,
2013191783Srmacklem				    op->nfso_fhlen)) {
2014191783Srmacklem				    dp->nfsdl_stateid = ndp->nfsdl_stateid;
2015191783Srmacklem				    dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit;
2016191783Srmacklem				    dp->nfsdl_ace = ndp->nfsdl_ace;
2017191783Srmacklem				    dp->nfsdl_change = ndp->nfsdl_change;
2018191783Srmacklem				    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2019191783Srmacklem				    if ((ndp->nfsdl_flags & NFSCLDL_RECALL))
2020191783Srmacklem					dp->nfsdl_flags |= NFSCLDL_RECALL;
2021191783Srmacklem				    FREE((caddr_t)ndp, M_NFSCLDELEG);
2022191783Srmacklem				    ndp = NULL;
2023191783Srmacklem				    break;
2024191783Srmacklem				}
2025191783Srmacklem			    }
2026191783Srmacklem			}
2027191783Srmacklem			if (ndp != NULL)
2028191783Srmacklem			    TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list);
2029191783Srmacklem
2030191783Srmacklem			/* and reclaim all byte range locks */
2031191783Srmacklem			lp = LIST_FIRST(&op->nfso_lock);
2032191783Srmacklem			while (lp != NULL) {
2033191783Srmacklem			    nlp = LIST_NEXT(lp, nfsl_list);
2034191783Srmacklem			    lp->nfsl_seqid = 0;
2035191783Srmacklem			    firstlock = 1;
2036191783Srmacklem			    lop = LIST_FIRST(&lp->nfsl_lock);
2037191783Srmacklem			    while (lop != NULL) {
2038191783Srmacklem				nlop = LIST_NEXT(lop, nfslo_list);
2039191783Srmacklem				if (lop->nfslo_end == NFS64BITSSET)
2040191783Srmacklem				    len = NFS64BITSSET;
2041191783Srmacklem				else
2042191783Srmacklem				    len = lop->nfslo_end - lop->nfslo_first;
2043191783Srmacklem				if (error != NFSERR_NOGRACE)
2044191783Srmacklem				    error = nfscl_trylock(nmp, NULL,
2045191783Srmacklem					op->nfso_fh, op->nfso_fhlen, lp,
2046191783Srmacklem					firstlock, 1, lop->nfslo_first, len,
2047191783Srmacklem					lop->nfslo_type, tcred, p);
2048191783Srmacklem				if (error != 0)
2049191783Srmacklem				    nfscl_freelock(lop, 0);
2050191783Srmacklem				else
2051191783Srmacklem				    firstlock = 0;
2052191783Srmacklem				lop = nlop;
2053191783Srmacklem			    }
2054191783Srmacklem			    /* If no locks, but a lockowner, just delete it. */
2055191783Srmacklem			    if (LIST_EMPTY(&lp->nfsl_lock))
2056191783Srmacklem				nfscl_freelockowner(lp, 0);
2057191783Srmacklem			    lp = nlp;
2058191783Srmacklem			}
2059191783Srmacklem		    } else {
2060191783Srmacklem			nfscl_freeopen(op, 0);
2061191783Srmacklem		    }
2062191783Srmacklem		}
2063191783Srmacklem		op = nop;
2064191783Srmacklem	    }
2065191783Srmacklem	    owp = nowp;
2066191783Srmacklem	}
2067191783Srmacklem
2068191783Srmacklem	/*
2069191783Srmacklem	 * Now, try and get any delegations not yet reclaimed by cobbling
2070191783Srmacklem	 * to-gether an appropriate open.
2071191783Srmacklem	 */
2072191783Srmacklem	nowp = NULL;
2073191783Srmacklem	dp = TAILQ_FIRST(&clp->nfsc_deleg);
2074191783Srmacklem	while (dp != NULL) {
2075191783Srmacklem	    ndp = TAILQ_NEXT(dp, nfsdl_list);
2076191783Srmacklem	    if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) {
2077191783Srmacklem		if (nowp == NULL) {
2078191783Srmacklem		    MALLOC(nowp, struct nfsclowner *,
2079191783Srmacklem			sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK);
2080191783Srmacklem		    /*
2081191783Srmacklem		     * Name must be as long an largest possible
2082191783Srmacklem		     * NFSV4CL_LOCKNAMELEN. 12 for now.
2083191783Srmacklem		     */
2084191783Srmacklem		    NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner,
2085191783Srmacklem			NFSV4CL_LOCKNAMELEN);
2086191783Srmacklem		    LIST_INIT(&nowp->nfsow_open);
2087191783Srmacklem		    nowp->nfsow_clp = clp;
2088191783Srmacklem		    nowp->nfsow_seqid = 0;
2089191783Srmacklem		    nowp->nfsow_defunct = 0;
2090191783Srmacklem		    nfscl_lockinit(&nowp->nfsow_rwlock);
2091191783Srmacklem		}
2092191783Srmacklem		nop = NULL;
2093191783Srmacklem		if (error != NFSERR_NOGRACE) {
2094191783Srmacklem		    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
2095191783Srmacklem			dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
2096191783Srmacklem		    nop->nfso_own = nowp;
2097191783Srmacklem		    if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
2098191783Srmacklem			nop->nfso_mode = NFSV4OPEN_ACCESSWRITE;
2099191783Srmacklem			delegtype = NFSV4OPEN_DELEGATEWRITE;
2100191783Srmacklem		    } else {
2101191783Srmacklem			nop->nfso_mode = NFSV4OPEN_ACCESSREAD;
2102191783Srmacklem			delegtype = NFSV4OPEN_DELEGATEREAD;
2103191783Srmacklem		    }
2104191783Srmacklem		    nop->nfso_opencnt = 0;
2105191783Srmacklem		    nop->nfso_posixlock = 1;
2106191783Srmacklem		    nop->nfso_fhlen = dp->nfsdl_fhlen;
2107191783Srmacklem		    NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen);
2108191783Srmacklem		    LIST_INIT(&nop->nfso_lock);
2109191783Srmacklem		    nop->nfso_stateid.seqid = 0;
2110191783Srmacklem		    nop->nfso_stateid.other[0] = 0;
2111191783Srmacklem		    nop->nfso_stateid.other[1] = 0;
2112191783Srmacklem		    nop->nfso_stateid.other[2] = 0;
2113191783Srmacklem		    newnfs_copycred(&dp->nfsdl_cred, tcred);
2114191783Srmacklem		    newnfs_copyincred(tcred, &nop->nfso_cred);
2115191783Srmacklem		    tdp = NULL;
2116191783Srmacklem		    error = nfscl_tryopen(nmp, NULL, nop->nfso_fh,
2117191783Srmacklem			nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen,
2118191783Srmacklem			nop->nfso_mode, nop, NULL, 0, &tdp, 1,
2119191783Srmacklem			delegtype, tcred, p);
2120191783Srmacklem		    if (tdp != NULL) {
2121191783Srmacklem			if ((tdp->nfsdl_flags & NFSCLDL_WRITE))
2122191783Srmacklem			    mode = NFSV4OPEN_ACCESSWRITE;
2123191783Srmacklem			else
2124191783Srmacklem			    mode = NFSV4OPEN_ACCESSREAD;
2125191783Srmacklem			if ((nop->nfso_mode & mode) == mode &&
2126191783Srmacklem			    nop->nfso_fhlen == tdp->nfsdl_fhlen &&
2127191783Srmacklem			    !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh,
2128191783Srmacklem			    nop->nfso_fhlen)) {
2129191783Srmacklem			    dp->nfsdl_stateid = tdp->nfsdl_stateid;
2130191783Srmacklem			    dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit;
2131191783Srmacklem			    dp->nfsdl_ace = tdp->nfsdl_ace;
2132191783Srmacklem			    dp->nfsdl_change = tdp->nfsdl_change;
2133191783Srmacklem			    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2134191783Srmacklem			    if ((tdp->nfsdl_flags & NFSCLDL_RECALL))
2135191783Srmacklem				dp->nfsdl_flags |= NFSCLDL_RECALL;
2136191783Srmacklem			    FREE((caddr_t)tdp, M_NFSCLDELEG);
2137191783Srmacklem			} else {
2138191783Srmacklem			    TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list);
2139191783Srmacklem			}
2140191783Srmacklem		    }
2141191783Srmacklem		}
2142191783Srmacklem		if (error) {
2143191783Srmacklem		    if (nop != NULL)
2144191783Srmacklem			FREE((caddr_t)nop, M_NFSCLOPEN);
2145191783Srmacklem		    /*
2146191783Srmacklem		     * Couldn't reclaim it, so throw the state
2147191783Srmacklem		     * away. Ouch!!
2148191783Srmacklem		     */
2149191783Srmacklem		    nfscl_cleandeleg(dp);
2150191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
2151191783Srmacklem		} else {
2152191783Srmacklem		    LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
2153191783Srmacklem		}
2154191783Srmacklem	    }
2155191783Srmacklem	    dp = ndp;
2156191783Srmacklem	}
2157191783Srmacklem
2158191783Srmacklem	/*
2159191783Srmacklem	 * Now, get rid of extra Opens and Delegations.
2160191783Srmacklem	 */
2161191783Srmacklem	LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) {
2162191783Srmacklem		do {
2163191783Srmacklem			newnfs_copycred(&op->nfso_cred, tcred);
2164191783Srmacklem			error = nfscl_tryclose(op, tcred, nmp, p);
2165191783Srmacklem			if (error == NFSERR_GRACE)
2166207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsexcls");
2167191783Srmacklem		} while (error == NFSERR_GRACE);
2168191783Srmacklem		LIST_REMOVE(op, nfso_list);
2169191783Srmacklem		FREE((caddr_t)op, M_NFSCLOPEN);
2170191783Srmacklem	}
2171191783Srmacklem	if (nowp != NULL)
2172191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
2173191783Srmacklem
2174191783Srmacklem	TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) {
2175191783Srmacklem		do {
2176191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, tcred);
2177191783Srmacklem			error = nfscl_trydelegreturn(dp, tcred, nmp, p);
2178191783Srmacklem			if (error == NFSERR_GRACE)
2179207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsexdlg");
2180191783Srmacklem		} while (error == NFSERR_GRACE);
2181191783Srmacklem		TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list);
2182191783Srmacklem		FREE((caddr_t)dp, M_NFSCLDELEG);
2183191783Srmacklem	}
2184191783Srmacklem
2185244042Srmacklem	/* For NFSv4.1 or later, do a RECLAIM_COMPLETE. */
2186244042Srmacklem	if (NFSHASNFSV4N(nmp))
2187244042Srmacklem		(void)nfsrpc_reclaimcomplete(nmp, cred, p);
2188244042Srmacklem
2189191783Srmacklem	NFSLOCKCLSTATE();
2190206818Srmacklem	clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG;
2191206818Srmacklem	wakeup(&clp->nfsc_flags);
2192191783Srmacklem	nfsv4_unlock(&clp->nfsc_lock, 0);
2193191783Srmacklem	NFSUNLOCKCLSTATE();
2194191783Srmacklem	NFSFREECRED(tcred);
2195191783Srmacklem}
2196191783Srmacklem
2197191783Srmacklem/*
2198191783Srmacklem * This function is called when a server replies with NFSERR_EXPIRED.
2199191783Srmacklem * It deletes all state for the client and does a fresh SetClientId/confirm.
2200191783Srmacklem * XXX Someday it should post a signal to the process(es) that hold the
2201191783Srmacklem * state, so they know that lock state has been lost.
2202191783Srmacklem */
2203191783SrmacklemAPPLESTATIC int
2204191783Srmacklemnfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
2205191783Srmacklem{
2206191783Srmacklem	struct nfsmount *nmp;
2207191783Srmacklem	struct ucred *cred;
2208191783Srmacklem	int igotlock = 0, error, trycnt;
2209191783Srmacklem
2210191783Srmacklem	/*
2211191783Srmacklem	 * If the clientid has gone away or a new SetClientid has already
2212191783Srmacklem	 * been done, just return ok.
2213191783Srmacklem	 */
2214191783Srmacklem	if (clp == NULL || clidrev != clp->nfsc_clientidrev)
2215191783Srmacklem		return (0);
2216191783Srmacklem
2217191783Srmacklem	/*
2218191783Srmacklem	 * First, lock the client structure, so everyone else will
2219191783Srmacklem	 * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so
2220191783Srmacklem	 * that only one thread does the work.
2221191783Srmacklem	 */
2222191783Srmacklem	NFSLOCKCLSTATE();
2223191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
2224191783Srmacklem	do {
2225191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2226222389Srmacklem		    NFSCLSTATEMUTEXPTR, NULL);
2227191783Srmacklem	} while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
2228191783Srmacklem	if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
2229191783Srmacklem		if (igotlock)
2230191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
2231191783Srmacklem		NFSUNLOCKCLSTATE();
2232191783Srmacklem		return (0);
2233191783Srmacklem	}
2234206880Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
2235191783Srmacklem	NFSUNLOCKCLSTATE();
2236191783Srmacklem
2237191783Srmacklem	nmp = clp->nfsc_nmp;
2238191783Srmacklem	if (nmp == NULL)
2239191783Srmacklem		panic("nfscl expired");
2240191783Srmacklem	cred = newnfs_getcred();
2241191783Srmacklem	trycnt = 5;
2242191783Srmacklem	do {
2243244042Srmacklem		error = nfsrpc_setclient(nmp, clp, 0, cred, p);
2244191783Srmacklem	} while ((error == NFSERR_STALECLIENTID ||
2245244042Srmacklem	     error == NFSERR_BADSESSION ||
2246191783Srmacklem	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
2247191783Srmacklem	if (error) {
2248191783Srmacklem		/*
2249191783Srmacklem		 * Clear out any state.
2250191783Srmacklem		 */
2251191783Srmacklem		nfscl_cleanclient(clp);
2252206880Srmacklem		NFSLOCKCLSTATE();
2253191783Srmacklem		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
2254191783Srmacklem		    NFSCLFLAGS_RECOVER);
2255191783Srmacklem	} else {
2256191783Srmacklem		/*
2257191783Srmacklem		 * Expire the state for the client.
2258191783Srmacklem		 */
2259191783Srmacklem		nfscl_expireclient(clp, nmp, cred, p);
2260206880Srmacklem		NFSLOCKCLSTATE();
2261191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
2262191783Srmacklem		clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2263191783Srmacklem	}
2264206880Srmacklem	clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG);
2265206880Srmacklem	wakeup(&clp->nfsc_flags);
2266191783Srmacklem	nfsv4_unlock(&clp->nfsc_lock, 0);
2267191783Srmacklem	NFSUNLOCKCLSTATE();
2268206880Srmacklem	NFSFREECRED(cred);
2269191783Srmacklem	return (error);
2270191783Srmacklem}
2271191783Srmacklem
2272191783Srmacklem/*
2273191783Srmacklem * This function inserts a lock in the list after insert_lop.
2274191783Srmacklem */
2275191783Srmacklemstatic void
2276191783Srmacklemnfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop,
2277191783Srmacklem    struct nfscllock *insert_lop, int local)
2278191783Srmacklem{
2279191783Srmacklem
2280191783Srmacklem	if ((struct nfscllockowner *)insert_lop == lp)
2281191783Srmacklem		LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list);
2282191783Srmacklem	else
2283191783Srmacklem		LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list);
2284191783Srmacklem	if (local)
2285191783Srmacklem		newnfsstats.cllocallocks++;
2286191783Srmacklem	else
2287191783Srmacklem		newnfsstats.cllocks++;
2288191783Srmacklem}
2289191783Srmacklem
2290191783Srmacklem/*
2291191783Srmacklem * This function updates the locking for a lock owner and given file. It
2292191783Srmacklem * maintains a list of lock ranges ordered on increasing file offset that
2293191783Srmacklem * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style).
2294191783Srmacklem * It always adds new_lop to the list and sometimes uses the one pointed
2295191783Srmacklem * at by other_lopp.
2296191783Srmacklem * Returns 1 if the locks were modified, 0 otherwise.
2297191783Srmacklem */
2298191783Srmacklemstatic int
2299191783Srmacklemnfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp,
2300191783Srmacklem    struct nfscllock **other_lopp, int local)
2301191783Srmacklem{
2302191783Srmacklem	struct nfscllock *new_lop = *new_lopp;
2303191783Srmacklem	struct nfscllock *lop, *tlop, *ilop;
2304191783Srmacklem	struct nfscllock *other_lop;
2305191783Srmacklem	int unlock = 0, modified = 0;
2306191783Srmacklem	u_int64_t tmp;
2307191783Srmacklem
2308191783Srmacklem	/*
2309191783Srmacklem	 * Work down the list until the lock is merged.
2310191783Srmacklem	 */
2311191783Srmacklem	if (new_lop->nfslo_type == F_UNLCK)
2312191783Srmacklem		unlock = 1;
2313191783Srmacklem	ilop = (struct nfscllock *)lp;
2314191783Srmacklem	lop = LIST_FIRST(&lp->nfsl_lock);
2315191783Srmacklem	while (lop != NULL) {
2316191783Srmacklem	    /*
2317191783Srmacklem	     * Only check locks for this file that aren't before the start of
2318191783Srmacklem	     * new lock's range.
2319191783Srmacklem	     */
2320191783Srmacklem	    if (lop->nfslo_end >= new_lop->nfslo_first) {
2321191783Srmacklem		if (new_lop->nfslo_end < lop->nfslo_first) {
2322191783Srmacklem		    /*
2323191783Srmacklem		     * If the new lock ends before the start of the
2324191783Srmacklem		     * current lock's range, no merge, just insert
2325191783Srmacklem		     * the new lock.
2326191783Srmacklem		     */
2327191783Srmacklem		    break;
2328191783Srmacklem		}
2329191783Srmacklem		if (new_lop->nfslo_type == lop->nfslo_type ||
2330191783Srmacklem		    (new_lop->nfslo_first <= lop->nfslo_first &&
2331191783Srmacklem		     new_lop->nfslo_end >= lop->nfslo_end)) {
2332191783Srmacklem		    /*
2333191783Srmacklem		     * This lock can be absorbed by the new lock/unlock.
2334191783Srmacklem		     * This happens when it covers the entire range
2335191783Srmacklem		     * of the old lock or is contiguous
2336191783Srmacklem		     * with the old lock and is of the same type or an
2337191783Srmacklem		     * unlock.
2338191783Srmacklem		     */
2339191783Srmacklem		    if (new_lop->nfslo_type != lop->nfslo_type ||
2340191783Srmacklem			new_lop->nfslo_first != lop->nfslo_first ||
2341191783Srmacklem			new_lop->nfslo_end != lop->nfslo_end)
2342191783Srmacklem			modified = 1;
2343191783Srmacklem		    if (lop->nfslo_first < new_lop->nfslo_first)
2344191783Srmacklem			new_lop->nfslo_first = lop->nfslo_first;
2345191783Srmacklem		    if (lop->nfslo_end > new_lop->nfslo_end)
2346191783Srmacklem			new_lop->nfslo_end = lop->nfslo_end;
2347191783Srmacklem		    tlop = lop;
2348191783Srmacklem		    lop = LIST_NEXT(lop, nfslo_list);
2349191783Srmacklem		    nfscl_freelock(tlop, local);
2350191783Srmacklem		    continue;
2351191783Srmacklem		}
2352191783Srmacklem
2353191783Srmacklem		/*
2354191783Srmacklem		 * All these cases are for contiguous locks that are not the
2355191783Srmacklem		 * same type, so they can't be merged.
2356191783Srmacklem		 */
2357191783Srmacklem		if (new_lop->nfslo_first <= lop->nfslo_first) {
2358191783Srmacklem		    /*
2359191783Srmacklem		     * This case is where the new lock overlaps with the
2360191783Srmacklem		     * first part of the old lock. Move the start of the
2361191783Srmacklem		     * old lock to just past the end of the new lock. The
2362191783Srmacklem		     * new lock will be inserted in front of the old, since
2363191783Srmacklem		     * ilop hasn't been updated. (We are done now.)
2364191783Srmacklem		     */
2365191783Srmacklem		    if (lop->nfslo_first != new_lop->nfslo_end) {
2366191783Srmacklem			lop->nfslo_first = new_lop->nfslo_end;
2367191783Srmacklem			modified = 1;
2368191783Srmacklem		    }
2369191783Srmacklem		    break;
2370191783Srmacklem		}
2371191783Srmacklem		if (new_lop->nfslo_end >= lop->nfslo_end) {
2372191783Srmacklem		    /*
2373191783Srmacklem		     * This case is where the new lock overlaps with the
2374191783Srmacklem		     * end of the old lock's range. Move the old lock's
2375191783Srmacklem		     * end to just before the new lock's first and insert
2376191783Srmacklem		     * the new lock after the old lock.
2377191783Srmacklem		     * Might not be done yet, since the new lock could
2378191783Srmacklem		     * overlap further locks with higher ranges.
2379191783Srmacklem		     */
2380191783Srmacklem		    if (lop->nfslo_end != new_lop->nfslo_first) {
2381191783Srmacklem			lop->nfslo_end = new_lop->nfslo_first;
2382191783Srmacklem			modified = 1;
2383191783Srmacklem		    }
2384191783Srmacklem		    ilop = lop;
2385191783Srmacklem		    lop = LIST_NEXT(lop, nfslo_list);
2386191783Srmacklem		    continue;
2387191783Srmacklem		}
2388191783Srmacklem		/*
2389191783Srmacklem		 * The final case is where the new lock's range is in the
2390191783Srmacklem		 * middle of the current lock's and splits the current lock
2391191783Srmacklem		 * up. Use *other_lopp to handle the second part of the
2392191783Srmacklem		 * split old lock range. (We are done now.)
2393191783Srmacklem		 * For unlock, we use new_lop as other_lop and tmp, since
2394191783Srmacklem		 * other_lop and new_lop are the same for this case.
2395191783Srmacklem		 * We noted the unlock case above, so we don't need
2396191783Srmacklem		 * new_lop->nfslo_type any longer.
2397191783Srmacklem		 */
2398191783Srmacklem		tmp = new_lop->nfslo_first;
2399191783Srmacklem		if (unlock) {
2400191783Srmacklem		    other_lop = new_lop;
2401191783Srmacklem		    *new_lopp = NULL;
2402191783Srmacklem		} else {
2403191783Srmacklem		    other_lop = *other_lopp;
2404191783Srmacklem		    *other_lopp = NULL;
2405191783Srmacklem		}
2406191783Srmacklem		other_lop->nfslo_first = new_lop->nfslo_end;
2407191783Srmacklem		other_lop->nfslo_end = lop->nfslo_end;
2408191783Srmacklem		other_lop->nfslo_type = lop->nfslo_type;
2409191783Srmacklem		lop->nfslo_end = tmp;
2410191783Srmacklem		nfscl_insertlock(lp, other_lop, lop, local);
2411191783Srmacklem		ilop = lop;
2412191783Srmacklem		modified = 1;
2413191783Srmacklem		break;
2414191783Srmacklem	    }
2415191783Srmacklem	    ilop = lop;
2416191783Srmacklem	    lop = LIST_NEXT(lop, nfslo_list);
2417191783Srmacklem	    if (lop == NULL)
2418191783Srmacklem		break;
2419191783Srmacklem	}
2420191783Srmacklem
2421191783Srmacklem	/*
2422191783Srmacklem	 * Insert the new lock in the list at the appropriate place.
2423191783Srmacklem	 */
2424191783Srmacklem	if (!unlock) {
2425191783Srmacklem		nfscl_insertlock(lp, new_lop, ilop, local);
2426191783Srmacklem		*new_lopp = NULL;
2427191783Srmacklem		modified = 1;
2428191783Srmacklem	}
2429191783Srmacklem	return (modified);
2430191783Srmacklem}
2431191783Srmacklem
2432191783Srmacklem/*
2433191783Srmacklem * This function must be run as a kernel thread.
2434191783Srmacklem * It does Renew Ops and recovery, when required.
2435191783Srmacklem */
2436191783SrmacklemAPPLESTATIC void
2437191783Srmacklemnfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
2438191783Srmacklem{
2439191783Srmacklem	struct nfsclowner *owp, *nowp;
2440191783Srmacklem	struct nfsclopen *op;
2441228217Srmacklem	struct nfscllockowner *lp, *nlp;
2442191783Srmacklem	struct nfscldeleghead dh;
2443191783Srmacklem	struct nfscldeleg *dp, *ndp;
2444191783Srmacklem	struct ucred *cred;
2445191783Srmacklem	u_int32_t clidrev;
2446191783Srmacklem	int error, cbpathdown, islept, igotlock, ret, clearok;
2447206818Srmacklem	uint32_t recover_done_time = 0;
2448245909Sjhb	time_t mytime;
2449227796Srmacklem	static time_t prevsec = 0;
2450228217Srmacklem	struct nfscllockownerfh *lfhp, *nlfhp;
2451228217Srmacklem	struct nfscllockownerfhhead lfh;
2452244042Srmacklem	struct nfscllayout *lyp, *nlyp;
2453244042Srmacklem	struct nfscldevinfo *dip, *ndip;
2454244042Srmacklem	struct nfscllayouthead rlh;
2455244042Srmacklem	struct nfsclrecalllayout *recallp;
2456244042Srmacklem	struct nfsclds *dsp;
2457191783Srmacklem
2458191783Srmacklem	cred = newnfs_getcred();
2459206690Srmacklem	NFSLOCKCLSTATE();
2460191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD;
2461206690Srmacklem	NFSUNLOCKCLSTATE();
2462191783Srmacklem	for(;;) {
2463191783Srmacklem		newnfs_setroot(cred);
2464191783Srmacklem		cbpathdown = 0;
2465206818Srmacklem		if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) {
2466206818Srmacklem			/*
2467206818Srmacklem			 * Only allow one recover within 1/2 of the lease
2468206818Srmacklem			 * duration (nfsc_renew).
2469206818Srmacklem			 */
2470206818Srmacklem			if (recover_done_time < NFSD_MONOSEC) {
2471206818Srmacklem				recover_done_time = NFSD_MONOSEC +
2472206818Srmacklem				    clp->nfsc_renew;
2473206818Srmacklem				nfscl_recover(clp, cred, p);
2474206818Srmacklem			} else {
2475206818Srmacklem				NFSLOCKCLSTATE();
2476206818Srmacklem				clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2477206818Srmacklem				NFSUNLOCKCLSTATE();
2478206818Srmacklem			}
2479206818Srmacklem		}
2480191783Srmacklem		if (clp->nfsc_expire <= NFSD_MONOSEC &&
2481191783Srmacklem		    (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
2482191783Srmacklem			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
2483191783Srmacklem			clidrev = clp->nfsc_clientidrev;
2484244042Srmacklem			error = nfsrpc_renew(clp,
2485244042Srmacklem			    TAILQ_FIRST(&clp->nfsc_nmp->nm_sess), cred, p);
2486191783Srmacklem			if (error == NFSERR_CBPATHDOWN)
2487191783Srmacklem			    cbpathdown = 1;
2488244042Srmacklem			else if (error == NFSERR_STALECLIENTID ||
2489244042Srmacklem			    error == NFSERR_BADSESSION) {
2490206690Srmacklem			    NFSLOCKCLSTATE();
2491191783Srmacklem			    clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2492206690Srmacklem			    NFSUNLOCKCLSTATE();
2493206690Srmacklem			} else if (error == NFSERR_EXPIRED)
2494191783Srmacklem			    (void) nfscl_hasexpired(clp, clidrev, p);
2495191783Srmacklem		}
2496191783Srmacklem
2497244042Srmacklem		/* Do renews for any DS sessions. */
2498244042Srmacklemcheckdsrenew:
2499244042Srmacklem		NFSLOCKMNT(clp->nfsc_nmp);
2500244042Srmacklem		/* Skip first entry, since the MDS is handled above. */
2501244042Srmacklem		dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess);
2502244042Srmacklem		if (dsp != NULL)
2503244042Srmacklem			dsp = TAILQ_NEXT(dsp, nfsclds_list);
2504244042Srmacklem		while (dsp != NULL) {
2505244042Srmacklem			if (dsp->nfsclds_expire <= NFSD_MONOSEC) {
2506244042Srmacklem				dsp->nfsclds_expire = NFSD_MONOSEC +
2507244042Srmacklem				    clp->nfsc_renew;
2508244042Srmacklem				NFSUNLOCKMNT(clp->nfsc_nmp);
2509244042Srmacklem				(void)nfsrpc_renew(clp, dsp, cred, p);
2510244042Srmacklem				goto checkdsrenew;
2511244042Srmacklem			}
2512244042Srmacklem			dsp = TAILQ_NEXT(dsp, nfsclds_list);
2513244042Srmacklem		}
2514244042Srmacklem		NFSUNLOCKMNT(clp->nfsc_nmp);
2515244042Srmacklem
2516191783Srmacklem		TAILQ_INIT(&dh);
2517191783Srmacklem		NFSLOCKCLSTATE();
2518191783Srmacklem		if (cbpathdown)
2519191783Srmacklem			/* It's a Total Recall! */
2520191783Srmacklem			nfscl_totalrecall(clp);
2521191783Srmacklem
2522191783Srmacklem		/*
2523191783Srmacklem		 * Now, handle defunct owners.
2524191783Srmacklem		 */
2525228217Srmacklem		LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
2526228217Srmacklem			if (LIST_EMPTY(&owp->nfsow_open)) {
2527228217Srmacklem				if (owp->nfsow_defunct != 0)
2528228217Srmacklem					nfscl_freeopenowner(owp, 0);
2529191783Srmacklem			}
2530191783Srmacklem		}
2531191783Srmacklem
2532191783Srmacklem		/*
2533191783Srmacklem		 * Do the recall on any delegations. To avoid trouble, always
2534191783Srmacklem		 * come back up here after having slept.
2535191783Srmacklem		 */
2536191783Srmacklem		igotlock = 0;
2537191783Srmacklemtryagain:
2538191783Srmacklem		dp = TAILQ_FIRST(&clp->nfsc_deleg);
2539191783Srmacklem		while (dp != NULL) {
2540191783Srmacklem			ndp = TAILQ_NEXT(dp, nfsdl_list);
2541191783Srmacklem			if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
2542191783Srmacklem				/*
2543191783Srmacklem				 * Wait for outstanding I/O ops to be done.
2544191783Srmacklem				 */
2545191783Srmacklem				if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
2546191783Srmacklem				    if (igotlock) {
2547191783Srmacklem					nfsv4_unlock(&clp->nfsc_lock, 0);
2548191783Srmacklem					igotlock = 0;
2549191783Srmacklem				    }
2550191783Srmacklem				    dp->nfsdl_rwlock.nfslock_lock |=
2551191783Srmacklem					NFSV4LOCK_WANTED;
2552191783Srmacklem				    (void) nfsmsleep(&dp->nfsdl_rwlock,
2553191783Srmacklem					NFSCLSTATEMUTEXPTR, PZERO, "nfscld",
2554191783Srmacklem					NULL);
2555191783Srmacklem				    goto tryagain;
2556191783Srmacklem				}
2557191783Srmacklem				while (!igotlock) {
2558191783Srmacklem				    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
2559222389Srmacklem					&islept, NFSCLSTATEMUTEXPTR, NULL);
2560191783Srmacklem				    if (islept)
2561191783Srmacklem					goto tryagain;
2562191783Srmacklem				}
2563191783Srmacklem				NFSUNLOCKCLSTATE();
2564191783Srmacklem				newnfs_copycred(&dp->nfsdl_cred, cred);
2565191783Srmacklem				ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
2566207082Srmacklem				    NULL, cred, p, 1);
2567191783Srmacklem				if (!ret) {
2568191783Srmacklem				    nfscl_cleandeleg(dp);
2569191783Srmacklem				    TAILQ_REMOVE(&clp->nfsc_deleg, dp,
2570191783Srmacklem					nfsdl_list);
2571191783Srmacklem				    LIST_REMOVE(dp, nfsdl_hash);
2572191783Srmacklem				    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2573191783Srmacklem				    nfscl_delegcnt--;
2574191783Srmacklem				    newnfsstats.cldelegates--;
2575191783Srmacklem				}
2576191783Srmacklem				NFSLOCKCLSTATE();
2577191783Srmacklem			}
2578191783Srmacklem			dp = ndp;
2579191783Srmacklem		}
2580191783Srmacklem
2581191783Srmacklem		/*
2582191783Srmacklem		 * Clear out old delegations, if we are above the high water
2583191783Srmacklem		 * mark. Only clear out ones with no state related to them.
2584191783Srmacklem		 * The tailq list is in LRU order.
2585191783Srmacklem		 */
2586191783Srmacklem		dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
2587191783Srmacklem		while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) {
2588191783Srmacklem		    ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
2589191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
2590191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock == 0 &&
2591191783Srmacklem			dp->nfsdl_timestamp < NFSD_MONOSEC &&
2592214406Srmacklem			(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
2593214406Srmacklem			  NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) {
2594191783Srmacklem			clearok = 1;
2595191783Srmacklem			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2596191783Srmacklem			    op = LIST_FIRST(&owp->nfsow_open);
2597195819Srmacklem			    if (op != NULL) {
2598191783Srmacklem				clearok = 0;
2599191783Srmacklem				break;
2600191783Srmacklem			    }
2601191783Srmacklem			}
2602191783Srmacklem			if (clearok) {
2603191783Srmacklem			    LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
2604191783Srmacklem				if (!LIST_EMPTY(&lp->nfsl_lock)) {
2605191783Srmacklem				    clearok = 0;
2606191783Srmacklem				    break;
2607191783Srmacklem				}
2608191783Srmacklem			    }
2609191783Srmacklem			}
2610191783Srmacklem			if (clearok) {
2611191783Srmacklem			    TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
2612191783Srmacklem			    LIST_REMOVE(dp, nfsdl_hash);
2613191783Srmacklem			    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2614191783Srmacklem			    nfscl_delegcnt--;
2615191783Srmacklem			    newnfsstats.cldelegates--;
2616191783Srmacklem			}
2617191783Srmacklem		    }
2618191783Srmacklem		    dp = ndp;
2619191783Srmacklem		}
2620191783Srmacklem		if (igotlock)
2621191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
2622244042Srmacklem
2623244042Srmacklem		/*
2624244042Srmacklem		 * Do the recall on any layouts. To avoid trouble, always
2625244042Srmacklem		 * come back up here after having slept.
2626244042Srmacklem		 */
2627244042Srmacklem		TAILQ_INIT(&rlh);
2628244042Srmacklemtryagain2:
2629244042Srmacklem		TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) {
2630244042Srmacklem			if ((lyp->nfsly_flags & NFSLY_RECALL) != 0) {
2631244042Srmacklem				/*
2632244042Srmacklem				 * Wait for outstanding I/O ops to be done.
2633244042Srmacklem				 */
2634244042Srmacklem				if (lyp->nfsly_lock.nfslock_usecnt > 0 ||
2635244042Srmacklem				    (lyp->nfsly_lock.nfslock_lock &
2636244042Srmacklem				     NFSV4LOCK_LOCK) != 0) {
2637244042Srmacklem					lyp->nfsly_lock.nfslock_lock |=
2638244042Srmacklem					    NFSV4LOCK_WANTED;
2639244042Srmacklem					(void)nfsmsleep(&lyp->nfsly_lock,
2640244042Srmacklem					    NFSCLSTATEMUTEXPTR, PZERO, "nfslyp",
2641244042Srmacklem					    NULL);
2642244042Srmacklem					goto tryagain2;
2643244042Srmacklem				}
2644244042Srmacklem				/* Move the layout to the recall list. */
2645244042Srmacklem				TAILQ_REMOVE(&clp->nfsc_layout, lyp,
2646244042Srmacklem				    nfsly_list);
2647244042Srmacklem				LIST_REMOVE(lyp, nfsly_hash);
2648244042Srmacklem				TAILQ_INSERT_HEAD(&rlh, lyp, nfsly_list);
2649244042Srmacklem
2650244042Srmacklem				/* Handle any layout commits. */
2651244042Srmacklem				if (!NFSHASNOLAYOUTCOMMIT(clp->nfsc_nmp) &&
2652244042Srmacklem				    (lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
2653244042Srmacklem					lyp->nfsly_flags &= ~NFSLY_WRITTEN;
2654244042Srmacklem					NFSUNLOCKCLSTATE();
2655244042Srmacklem					NFSCL_DEBUG(3, "do layoutcommit\n");
2656244042Srmacklem					nfscl_dolayoutcommit(clp->nfsc_nmp, lyp,
2657244042Srmacklem					    cred, p);
2658244042Srmacklem					NFSLOCKCLSTATE();
2659244042Srmacklem					goto tryagain2;
2660244042Srmacklem				}
2661244042Srmacklem			}
2662244042Srmacklem		}
2663244042Srmacklem
2664244042Srmacklem		/* Now, look for stale layouts. */
2665244042Srmacklem		lyp = TAILQ_LAST(&clp->nfsc_layout, nfscllayouthead);
2666244042Srmacklem		while (lyp != NULL) {
2667244042Srmacklem			nlyp = TAILQ_PREV(lyp, nfscllayouthead, nfsly_list);
2668244042Srmacklem			if (lyp->nfsly_timestamp < NFSD_MONOSEC &&
2669244042Srmacklem			    (lyp->nfsly_flags & NFSLY_RECALL) == 0 &&
2670244042Srmacklem			    lyp->nfsly_lock.nfslock_usecnt == 0 &&
2671244042Srmacklem			    lyp->nfsly_lock.nfslock_lock == 0) {
2672244042Srmacklem				NFSCL_DEBUG(4, "ret stale lay=%d\n",
2673244042Srmacklem				    nfscl_layoutcnt);
2674244042Srmacklem				recallp = malloc(sizeof(*recallp),
2675244042Srmacklem				    M_NFSLAYRECALL, M_NOWAIT);
2676244042Srmacklem				if (recallp == NULL)
2677244042Srmacklem					break;
2678244042Srmacklem				(void)nfscl_layoutrecall(NFSLAYOUTRETURN_FILE,
2679244042Srmacklem				    lyp, NFSLAYOUTIOMODE_ANY, 0, UINT64_MAX,
2680244042Srmacklem				    lyp->nfsly_stateid.seqid, recallp);
2681244042Srmacklem			}
2682244042Srmacklem			lyp = nlyp;
2683244042Srmacklem		}
2684244042Srmacklem
2685244042Srmacklem		/*
2686244042Srmacklem		 * Free up any unreferenced device info structures.
2687244042Srmacklem		 */
2688244042Srmacklem		LIST_FOREACH_SAFE(dip, &clp->nfsc_devinfo, nfsdi_list, ndip) {
2689244042Srmacklem			if (dip->nfsdi_layoutrefs == 0 &&
2690244042Srmacklem			    dip->nfsdi_refcnt == 0) {
2691244042Srmacklem				NFSCL_DEBUG(4, "freeing devinfo\n");
2692244042Srmacklem				LIST_REMOVE(dip, nfsdi_list);
2693244042Srmacklem				nfscl_freedevinfo(dip);
2694244042Srmacklem			}
2695244042Srmacklem		}
2696191783Srmacklem		NFSUNLOCKCLSTATE();
2697191783Srmacklem
2698244042Srmacklem		/* Do layout return(s), as required. */
2699244042Srmacklem		TAILQ_FOREACH_SAFE(lyp, &rlh, nfsly_list, nlyp) {
2700244042Srmacklem			TAILQ_REMOVE(&rlh, lyp, nfsly_list);
2701244042Srmacklem			NFSCL_DEBUG(4, "ret layout\n");
2702244042Srmacklem			nfscl_layoutreturn(clp->nfsc_nmp, lyp, cred, p);
2703244042Srmacklem			nfscl_freelayout(lyp);
2704244042Srmacklem		}
2705244042Srmacklem
2706191783Srmacklem		/*
2707191783Srmacklem		 * Delegreturn any delegations cleaned out or recalled.
2708191783Srmacklem		 */
2709191783Srmacklem		TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
2710191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
2711191783Srmacklem			(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2712191783Srmacklem			TAILQ_REMOVE(&dh, dp, nfsdl_list);
2713191783Srmacklem			FREE((caddr_t)dp, M_NFSCLDELEG);
2714191783Srmacklem		}
2715191783Srmacklem
2716228217Srmacklem		SLIST_INIT(&lfh);
2717191783Srmacklem		/*
2718227744Srmacklem		 * Call nfscl_cleanupkext() once per second to check for
2719227744Srmacklem		 * open/lock owners where the process has exited.
2720191783Srmacklem		 */
2721245909Sjhb		mytime = NFSD_MONOSEC;
2722245909Sjhb		if (prevsec != mytime) {
2723245909Sjhb			prevsec = mytime;
2724228217Srmacklem			nfscl_cleanupkext(clp, &lfh);
2725191783Srmacklem		}
2726191783Srmacklem
2727228217Srmacklem		/*
2728228217Srmacklem		 * Do a ReleaseLockOwner for all lock owners where the
2729228217Srmacklem		 * associated process no longer exists, as found by
2730228217Srmacklem		 * nfscl_cleanupkext().
2731228217Srmacklem		 */
2732228217Srmacklem		newnfs_setroot(cred);
2733228217Srmacklem		SLIST_FOREACH_SAFE(lfhp, &lfh, nfslfh_list, nlfhp) {
2734228217Srmacklem			LIST_FOREACH_SAFE(lp, &lfhp->nfslfh_lock, nfsl_list,
2735228217Srmacklem			    nlp) {
2736228217Srmacklem				(void)nfsrpc_rellockown(clp->nfsc_nmp, lp,
2737228217Srmacklem				    lfhp->nfslfh_fh, lfhp->nfslfh_len, cred,
2738228217Srmacklem				    p);
2739228217Srmacklem				nfscl_freelockowner(lp, 0);
2740228217Srmacklem			}
2741228217Srmacklem			free(lfhp, M_TEMP);
2742228217Srmacklem		}
2743228217Srmacklem		SLIST_INIT(&lfh);
2744228217Srmacklem
2745222389Srmacklem		NFSLOCKCLSTATE();
2746191783Srmacklem		if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
2747222389Srmacklem			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl",
2748222389Srmacklem			    hz);
2749191783Srmacklem		if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
2750222389Srmacklem			clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
2751222389Srmacklem			NFSUNLOCKCLSTATE();
2752191783Srmacklem			NFSFREECRED(cred);
2753191783Srmacklem			wakeup((caddr_t)clp);
2754191783Srmacklem			return;
2755191783Srmacklem		}
2756222389Srmacklem		NFSUNLOCKCLSTATE();
2757191783Srmacklem	}
2758191783Srmacklem}
2759191783Srmacklem
2760191783Srmacklem/*
2761244042Srmacklem * Initiate state recovery. Called when NFSERR_STALECLIENTID,
2762244042Srmacklem * NFSERR_STALESTATEID or NFSERR_BADSESSION is received.
2763191783Srmacklem */
2764191783SrmacklemAPPLESTATIC void
2765191783Srmacklemnfscl_initiate_recovery(struct nfsclclient *clp)
2766191783Srmacklem{
2767191783Srmacklem
2768191783Srmacklem	if (clp == NULL)
2769191783Srmacklem		return;
2770191783Srmacklem	NFSLOCKCLSTATE();
2771191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2772191783Srmacklem	NFSUNLOCKCLSTATE();
2773191783Srmacklem	wakeup((caddr_t)clp);
2774191783Srmacklem}
2775191783Srmacklem
2776191783Srmacklem/*
2777191783Srmacklem * Dump out the state stuff for debugging.
2778191783Srmacklem */
2779191783SrmacklemAPPLESTATIC void
2780191783Srmacklemnfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
2781191783Srmacklem    int lockowner, int locks)
2782191783Srmacklem{
2783191783Srmacklem	struct nfsclclient *clp;
2784191783Srmacklem	struct nfsclowner *owp;
2785191783Srmacklem	struct nfsclopen *op;
2786191783Srmacklem	struct nfscllockowner *lp;
2787191783Srmacklem	struct nfscllock *lop;
2788191783Srmacklem	struct nfscldeleg *dp;
2789191783Srmacklem
2790191783Srmacklem	clp = nmp->nm_clp;
2791191783Srmacklem	if (clp == NULL) {
2792191783Srmacklem		printf("nfscl dumpstate NULL clp\n");
2793191783Srmacklem		return;
2794191783Srmacklem	}
2795191783Srmacklem	NFSLOCKCLSTATE();
2796191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2797191783Srmacklem	  LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2798191783Srmacklem	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2799191783Srmacklem		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2800191783Srmacklem		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2801191783Srmacklem		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2802191783Srmacklem		    owp->nfsow_seqid);
2803191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2804191783Srmacklem		if (opens)
2805191783Srmacklem		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2806191783Srmacklem			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2807191783Srmacklem			op->nfso_stateid.other[2], op->nfso_opencnt,
2808191783Srmacklem			op->nfso_fh[12]);
2809191783Srmacklem		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2810191783Srmacklem		    if (lockowner)
2811191783Srmacklem			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2812191783Srmacklem			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2813191783Srmacklem			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2814191783Srmacklem			    lp->nfsl_seqid,
2815191783Srmacklem			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2816191783Srmacklem			    lp->nfsl_stateid.other[2]);
2817191783Srmacklem		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2818191783Srmacklem			if (locks)
2819191783Srmacklem#ifdef __FreeBSD__
2820191783Srmacklem			    printf("lck typ=%d fst=%ju end=%ju\n",
2821191783Srmacklem				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2822191783Srmacklem				(intmax_t)lop->nfslo_end);
2823191783Srmacklem#else
2824191783Srmacklem			    printf("lck typ=%d fst=%qd end=%qd\n",
2825191783Srmacklem				lop->nfslo_type, lop->nfslo_first,
2826191783Srmacklem				lop->nfslo_end);
2827191783Srmacklem#endif
2828191783Srmacklem		    }
2829191783Srmacklem		}
2830191783Srmacklem	    }
2831191783Srmacklem	  }
2832191783Srmacklem	}
2833191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2834191783Srmacklem	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2835191783Srmacklem		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2836191783Srmacklem		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2837191783Srmacklem		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2838191783Srmacklem		    owp->nfsow_seqid);
2839191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2840191783Srmacklem		if (opens)
2841191783Srmacklem		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2842191783Srmacklem			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2843191783Srmacklem			op->nfso_stateid.other[2], op->nfso_opencnt,
2844191783Srmacklem			op->nfso_fh[12]);
2845191783Srmacklem		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2846191783Srmacklem		    if (lockowner)
2847191783Srmacklem			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2848191783Srmacklem			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2849191783Srmacklem			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2850191783Srmacklem			    lp->nfsl_seqid,
2851191783Srmacklem			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2852191783Srmacklem			    lp->nfsl_stateid.other[2]);
2853191783Srmacklem		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2854191783Srmacklem			if (locks)
2855191783Srmacklem#ifdef __FreeBSD__
2856191783Srmacklem			    printf("lck typ=%d fst=%ju end=%ju\n",
2857191783Srmacklem				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2858191783Srmacklem				(intmax_t)lop->nfslo_end);
2859191783Srmacklem#else
2860191783Srmacklem			    printf("lck typ=%d fst=%qd end=%qd\n",
2861191783Srmacklem				lop->nfslo_type, lop->nfslo_first,
2862191783Srmacklem				lop->nfslo_end);
2863191783Srmacklem#endif
2864191783Srmacklem		    }
2865191783Srmacklem		}
2866191783Srmacklem	    }
2867191783Srmacklem	}
2868191783Srmacklem	NFSUNLOCKCLSTATE();
2869191783Srmacklem}
2870191783Srmacklem
2871191783Srmacklem/*
2872191783Srmacklem * Check for duplicate open owners and opens.
2873191783Srmacklem * (Only used as a diagnostic aid.)
2874191783Srmacklem */
2875191783SrmacklemAPPLESTATIC void
2876191783Srmacklemnfscl_dupopen(vnode_t vp, int dupopens)
2877191783Srmacklem{
2878191783Srmacklem	struct nfsclclient *clp;
2879191783Srmacklem	struct nfsclowner *owp, *owp2;
2880191783Srmacklem	struct nfsclopen *op, *op2;
2881191783Srmacklem	struct nfsfh *nfhp;
2882191783Srmacklem
2883191783Srmacklem	clp = VFSTONFS(vnode_mount(vp))->nm_clp;
2884191783Srmacklem	if (clp == NULL) {
2885191783Srmacklem		printf("nfscl dupopen NULL clp\n");
2886191783Srmacklem		return;
2887191783Srmacklem	}
2888191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
2889191783Srmacklem	NFSLOCKCLSTATE();
2890191783Srmacklem
2891191783Srmacklem	/*
2892191783Srmacklem	 * First, search for duplicate owners.
2893191783Srmacklem	 * These should never happen!
2894191783Srmacklem	 */
2895191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2896191783Srmacklem	    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2897191783Srmacklem		if (owp != owp2 &&
2898191783Srmacklem		    !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
2899191783Srmacklem		    NFSV4CL_LOCKNAMELEN)) {
2900191783Srmacklem			NFSUNLOCKCLSTATE();
2901191783Srmacklem			printf("DUP OWNER\n");
2902191783Srmacklem			nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0);
2903191783Srmacklem			return;
2904191783Srmacklem		}
2905191783Srmacklem	    }
2906191783Srmacklem	}
2907191783Srmacklem
2908191783Srmacklem	/*
2909191783Srmacklem	 * Now, search for duplicate stateids.
2910191783Srmacklem	 * These shouldn't happen, either.
2911191783Srmacklem	 */
2912191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2913191783Srmacklem	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2914191783Srmacklem		LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2915191783Srmacklem		    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2916191783Srmacklem			if (op != op2 &&
2917191783Srmacklem			    (op->nfso_stateid.other[0] != 0 ||
2918191783Srmacklem			     op->nfso_stateid.other[1] != 0 ||
2919191783Srmacklem			     op->nfso_stateid.other[2] != 0) &&
2920191783Srmacklem			    op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
2921191783Srmacklem			    op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
2922191783Srmacklem			    op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
2923191783Srmacklem			    NFSUNLOCKCLSTATE();
2924191783Srmacklem			    printf("DUP STATEID\n");
2925191783Srmacklem			    nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0,
2926191783Srmacklem				0);
2927191783Srmacklem			    return;
2928191783Srmacklem			}
2929191783Srmacklem		    }
2930191783Srmacklem		}
2931191783Srmacklem	    }
2932191783Srmacklem	}
2933191783Srmacklem
2934191783Srmacklem	/*
2935191783Srmacklem	 * Now search for duplicate opens.
2936191783Srmacklem	 * Duplicate opens for the same owner
2937191783Srmacklem	 * should never occur. Other duplicates are
2938191783Srmacklem	 * possible and are checked for if "dupopens"
2939191783Srmacklem	 * is true.
2940191783Srmacklem	 */
2941191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2942191783Srmacklem	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2943191783Srmacklem		if (nfhp->nfh_len == op2->nfso_fhlen &&
2944191783Srmacklem		    !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
2945191783Srmacklem		    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2946191783Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2947191783Srmacklem			    if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
2948191783Srmacklem				!NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
2949191783Srmacklem				(!NFSBCMP(op->nfso_own->nfsow_owner,
2950191783Srmacklem				 op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
2951191783Srmacklem				 dupopens)) {
2952191783Srmacklem				if (!NFSBCMP(op->nfso_own->nfsow_owner,
2953191783Srmacklem				    op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
2954191783Srmacklem				    NFSUNLOCKCLSTATE();
2955191783Srmacklem				    printf("BADDUP OPEN\n");
2956191783Srmacklem				} else {
2957191783Srmacklem				    NFSUNLOCKCLSTATE();
2958191783Srmacklem				    printf("DUP OPEN\n");
2959191783Srmacklem				}
2960191783Srmacklem				nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1,
2961191783Srmacklem				    0, 0);
2962191783Srmacklem				return;
2963191783Srmacklem			    }
2964191783Srmacklem			}
2965191783Srmacklem		    }
2966191783Srmacklem		}
2967191783Srmacklem	    }
2968191783Srmacklem	}
2969191783Srmacklem	NFSUNLOCKCLSTATE();
2970191783Srmacklem}
2971191783Srmacklem
2972191783Srmacklem/*
2973191783Srmacklem * During close, find an open that needs to be dereferenced and
2974191783Srmacklem * dereference it. If there are no more opens for this file,
2975195510Srmacklem * log a message to that effect.
2976195510Srmacklem * Opens aren't actually Close'd until VOP_INACTIVE() is performed
2977195510Srmacklem * on the file's vnode.
2978191783Srmacklem * This is the safe way, since it is difficult to identify
2979195510Srmacklem * which open the close is for and I/O can be performed after the
2980195510Srmacklem * close(2) system call when a file is mmap'd.
2981191783Srmacklem * If it returns 0 for success, there will be a referenced
2982195510Srmacklem * clp returned via clpp.
2983191783Srmacklem */
2984191783SrmacklemAPPLESTATIC int
2985195510Srmacklemnfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
2986191783Srmacklem{
2987191783Srmacklem	struct nfsclclient *clp;
2988195510Srmacklem	struct nfsclowner *owp;
2989195510Srmacklem	struct nfsclopen *op;
2990191783Srmacklem	struct nfscldeleg *dp;
2991191783Srmacklem	struct nfsfh *nfhp;
2992195510Srmacklem	int error, notdecr;
2993191783Srmacklem
2994244042Srmacklem	error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp);
2995191783Srmacklem	if (error)
2996191783Srmacklem		return (error);
2997191783Srmacklem	*clpp = clp;
2998191783Srmacklem
2999191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
3000191783Srmacklem	notdecr = 1;
3001191783Srmacklem	NFSLOCKCLSTATE();
3002191783Srmacklem	/*
3003191783Srmacklem	 * First, look for one under a delegation that was locally issued
3004191783Srmacklem	 * and just decrement the opencnt for it. Since all my Opens against
3005191783Srmacklem	 * the server are DENY_NONE, I don't see a problem with hanging
3006191783Srmacklem	 * onto them. (It is much easier to use one of the extant Opens
3007191783Srmacklem	 * that I already have on the server when a Delegation is recalled
3008195510Srmacklem	 * than to do fresh Opens.) Someday, I might need to rethink this, but.
3009191783Srmacklem	 */
3010191783Srmacklem	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3011191783Srmacklem	if (dp != NULL) {
3012191783Srmacklem		LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3013191783Srmacklem			op = LIST_FIRST(&owp->nfsow_open);
3014191783Srmacklem			if (op != NULL) {
3015191783Srmacklem				/*
3016191783Srmacklem				 * Since a delegation is for a file, there
3017191783Srmacklem				 * should never be more than one open for
3018191783Srmacklem				 * each openowner.
3019191783Srmacklem				 */
3020191783Srmacklem				if (LIST_NEXT(op, nfso_list) != NULL)
3021191783Srmacklem					panic("nfscdeleg opens");
3022191783Srmacklem				if (notdecr && op->nfso_opencnt > 0) {
3023191783Srmacklem					notdecr = 0;
3024191783Srmacklem					op->nfso_opencnt--;
3025191783Srmacklem					break;
3026191783Srmacklem				}
3027191783Srmacklem			}
3028191783Srmacklem		}
3029191783Srmacklem	}
3030191783Srmacklem
3031191783Srmacklem	/* Now process the opens against the server. */
3032191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3033195510Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3034192337Srmacklem			if (op->nfso_fhlen == nfhp->nfh_len &&
3035192337Srmacklem			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3036192337Srmacklem			    nfhp->nfh_len)) {
3037192337Srmacklem				/* Found an open, decrement cnt if possible */
3038192337Srmacklem				if (notdecr && op->nfso_opencnt > 0) {
3039192337Srmacklem					notdecr = 0;
3040192337Srmacklem					op->nfso_opencnt--;
3041192337Srmacklem				}
3042192337Srmacklem				/*
3043195510Srmacklem				 * There are more opens, so just return.
3044192337Srmacklem				 */
3045192337Srmacklem				if (op->nfso_opencnt > 0) {
3046192337Srmacklem					NFSUNLOCKCLSTATE();
3047192337Srmacklem					return (0);
3048192337Srmacklem				}
3049191783Srmacklem			}
3050191783Srmacklem		}
3051191783Srmacklem	}
3052195510Srmacklem	NFSUNLOCKCLSTATE();
3053195510Srmacklem	if (notdecr)
3054195510Srmacklem		printf("nfscl: never fnd open\n");
3055195510Srmacklem	return (0);
3056195510Srmacklem}
3057191783Srmacklem
3058195510SrmacklemAPPLESTATIC int
3059195510Srmacklemnfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
3060195510Srmacklem{
3061195510Srmacklem	struct nfsclclient *clp;
3062195510Srmacklem	struct nfsclowner *owp, *nowp;
3063195510Srmacklem	struct nfsclopen *op;
3064195510Srmacklem	struct nfscldeleg *dp;
3065195510Srmacklem	struct nfsfh *nfhp;
3066195510Srmacklem	int error;
3067195510Srmacklem
3068244042Srmacklem	error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp);
3069195510Srmacklem	if (error)
3070195510Srmacklem		return (error);
3071195510Srmacklem	*clpp = clp;
3072195510Srmacklem
3073195510Srmacklem	nfhp = VTONFS(vp)->n_fhp;
3074195510Srmacklem	NFSLOCKCLSTATE();
3075195510Srmacklem	/*
3076195510Srmacklem	 * First get rid of the local Open structures, which should be no
3077195510Srmacklem	 * longer in use.
3078195510Srmacklem	 */
3079195510Srmacklem	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3080195510Srmacklem	if (dp != NULL) {
3081195510Srmacklem		LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
3082191783Srmacklem			op = LIST_FIRST(&owp->nfsow_open);
3083195510Srmacklem			if (op != NULL) {
3084195510Srmacklem				KASSERT((op->nfso_opencnt == 0),
3085195510Srmacklem				    ("nfscl: bad open cnt on deleg"));
3086195510Srmacklem				nfscl_freeopen(op, 1);
3087191783Srmacklem			}
3088195510Srmacklem			nfscl_freeopenowner(owp, 1);
3089191783Srmacklem		}
3090195510Srmacklem	}
3091195510Srmacklem
3092244042Srmacklem	/* Return any layouts marked return on close. */
3093244042Srmacklem	nfscl_retoncloselayout(clp, nfhp->nfh_fh, nfhp->nfh_len);
3094244042Srmacklem
3095195510Srmacklem	/* Now process the opens against the server. */
3096195510Srmacklemlookformore:
3097195510Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3098195510Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
3099195510Srmacklem		while (op != NULL) {
3100195510Srmacklem			if (op->nfso_fhlen == nfhp->nfh_len &&
3101195510Srmacklem			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3102195510Srmacklem			    nfhp->nfh_len)) {
3103195510Srmacklem				/* Found an open, close it. */
3104195510Srmacklem				KASSERT((op->nfso_opencnt == 0),
3105195510Srmacklem				    ("nfscl: bad open cnt on server"));
3106195510Srmacklem				NFSUNLOCKCLSTATE();
3107195510Srmacklem				nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op,
3108195510Srmacklem				    p);
3109195510Srmacklem				NFSLOCKCLSTATE();
3110195510Srmacklem				goto lookformore;
3111191783Srmacklem			}
3112195510Srmacklem			op = LIST_NEXT(op, nfso_list);
3113191783Srmacklem		}
3114191783Srmacklem	}
3115191783Srmacklem	NFSUNLOCKCLSTATE();
3116191783Srmacklem	return (0);
3117191783Srmacklem}
3118191783Srmacklem
3119191783Srmacklem/*
3120191783Srmacklem * Return all delegations on this client.
3121191783Srmacklem * (Must be called with client sleep lock.)
3122191783Srmacklem */
3123191783Srmacklemstatic void
3124191783Srmacklemnfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
3125191783Srmacklem{
3126191783Srmacklem	struct nfscldeleg *dp, *ndp;
3127191783Srmacklem	struct ucred *cred;
3128191783Srmacklem
3129191783Srmacklem	cred = newnfs_getcred();
3130191783Srmacklem	TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
3131191783Srmacklem		nfscl_cleandeleg(dp);
3132191783Srmacklem		(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
3133191783Srmacklem		nfscl_freedeleg(&clp->nfsc_deleg, dp);
3134191783Srmacklem	}
3135191783Srmacklem	NFSFREECRED(cred);
3136191783Srmacklem}
3137191783Srmacklem
3138191783Srmacklem/*
3139191783Srmacklem * Do a callback RPC.
3140191783Srmacklem */
3141191783SrmacklemAPPLESTATIC void
3142191783Srmacklemnfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
3143191783Srmacklem{
3144244042Srmacklem	int clist, gotseq_ok, i, j, k, op, rcalls;
3145191783Srmacklem	u_int32_t *tl;
3146191783Srmacklem	struct nfsclclient *clp;
3147191783Srmacklem	struct nfscldeleg *dp = NULL;
3148244042Srmacklem	int numops, taglen = -1, error = 0, trunc;
3149191783Srmacklem	u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident;
3150191783Srmacklem	u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
3151191783Srmacklem	vnode_t vp = NULL;
3152191783Srmacklem	struct nfsnode *np;
3153191783Srmacklem	struct vattr va;
3154191783Srmacklem	struct nfsfh *nfhp;
3155191783Srmacklem	mount_t mp;
3156191783Srmacklem	nfsattrbit_t attrbits, rattrbits;
3157191783Srmacklem	nfsv4stateid_t stateid;
3158244042Srmacklem	uint32_t seqid, slotid = 0, highslot, cachethis;
3159244042Srmacklem	uint8_t sessionid[NFSX_V4SESSIONID];
3160244042Srmacklem	struct mbuf *rep;
3161244042Srmacklem	struct nfscllayout *lyp;
3162244042Srmacklem	uint64_t filesid[2], len, off;
3163244042Srmacklem	int changed, gotone, laytype, recalltype;
3164244042Srmacklem	uint32_t iomode;
3165244042Srmacklem	struct nfsclrecalllayout *recallp = NULL;
3166191783Srmacklem
3167244042Srmacklem	gotseq_ok = 0;
3168191783Srmacklem	nfsrvd_rephead(nd);
3169191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3170191783Srmacklem	taglen = fxdr_unsigned(int, *tl);
3171191783Srmacklem	if (taglen < 0) {
3172191783Srmacklem		error = EBADRPC;
3173191783Srmacklem		goto nfsmout;
3174191783Srmacklem	}
3175191783Srmacklem	if (taglen <= NFSV4_SMALLSTR)
3176191783Srmacklem		tagstr = tag;
3177191783Srmacklem	else
3178191783Srmacklem		tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
3179191783Srmacklem	error = nfsrv_mtostr(nd, tagstr, taglen);
3180191783Srmacklem	if (error) {
3181191783Srmacklem		if (taglen > NFSV4_SMALLSTR)
3182191783Srmacklem			free(tagstr, M_TEMP);
3183191783Srmacklem		taglen = -1;
3184191783Srmacklem		goto nfsmout;
3185191783Srmacklem	}
3186191783Srmacklem	(void) nfsm_strtom(nd, tag, taglen);
3187191783Srmacklem	if (taglen > NFSV4_SMALLSTR) {
3188191783Srmacklem		free(tagstr, M_TEMP);
3189191783Srmacklem	}
3190191783Srmacklem	NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
3191191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3192191783Srmacklem	minorvers = fxdr_unsigned(u_int32_t, *tl++);
3193244042Srmacklem	if (minorvers != NFSV4_MINORVERSION && minorvers != NFSV41_MINORVERSION)
3194191783Srmacklem		nd->nd_repstat = NFSERR_MINORVERMISMATCH;
3195191783Srmacklem	cbident = fxdr_unsigned(u_int32_t, *tl++);
3196191783Srmacklem	if (nd->nd_repstat)
3197191783Srmacklem		numops = 0;
3198191783Srmacklem	else
3199191783Srmacklem		numops = fxdr_unsigned(int, *tl);
3200191783Srmacklem	/*
3201191783Srmacklem	 * Loop around doing the sub ops.
3202191783Srmacklem	 */
3203191783Srmacklem	for (i = 0; i < numops; i++) {
3204191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3205191783Srmacklem		NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
3206191783Srmacklem		*repp++ = *tl;
3207191783Srmacklem		op = fxdr_unsigned(int, *tl);
3208244042Srmacklem		if (op < NFSV4OP_CBGETATTR ||
3209244042Srmacklem		   (op > NFSV4OP_CBRECALL && minorvers == NFSV4_MINORVERSION) ||
3210244042Srmacklem		   (op > NFSV4OP_CBNOTIFYDEVID &&
3211244042Srmacklem		    minorvers == NFSV41_MINORVERSION)) {
3212191783Srmacklem		    nd->nd_repstat = NFSERR_OPILLEGAL;
3213191783Srmacklem		    *repp = nfscl_errmap(nd);
3214191783Srmacklem		    retops++;
3215191783Srmacklem		    break;
3216191783Srmacklem		}
3217191783Srmacklem		nd->nd_procnum = op;
3218244042Srmacklem		if (op < NFSV4OP_CBNOPS)
3219244042Srmacklem			newnfsstats.cbrpccnt[nd->nd_procnum]++;
3220191783Srmacklem		switch (op) {
3221191783Srmacklem		case NFSV4OP_CBGETATTR:
3222244042Srmacklem			NFSCL_DEBUG(4, "cbgetattr\n");
3223244042Srmacklem			mp = NULL;
3224244042Srmacklem			vp = NULL;
3225191783Srmacklem			error = nfsm_getfh(nd, &nfhp);
3226191783Srmacklem			if (!error)
3227191783Srmacklem				error = nfsrv_getattrbits(nd, &attrbits,
3228191783Srmacklem				    NULL, NULL);
3229244042Srmacklem			if (error == 0 && i == 0 &&
3230244042Srmacklem			    minorvers != NFSV4_MINORVERSION)
3231244042Srmacklem				error = NFSERR_OPNOTINSESS;
3232191783Srmacklem			if (!error) {
3233244042Srmacklem				mp = nfscl_getmnt(minorvers, sessionid, cbident,
3234244042Srmacklem				    &clp);
3235191783Srmacklem				if (mp == NULL)
3236191783Srmacklem					error = NFSERR_SERVERFAULT;
3237191783Srmacklem			}
3238191783Srmacklem			if (!error) {
3239244042Srmacklem				error = nfscl_ngetreopen(mp, nfhp->nfh_fh,
3240244042Srmacklem				    nfhp->nfh_len, p, &np);
3241244042Srmacklem				if (!error)
3242244042Srmacklem					vp = NFSTOV(np);
3243244042Srmacklem			}
3244244042Srmacklem			if (!error) {
3245244042Srmacklem				NFSZERO_ATTRBIT(&rattrbits);
3246191783Srmacklem				NFSLOCKCLSTATE();
3247244042Srmacklem				dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3248244042Srmacklem				    nfhp->nfh_len);
3249244042Srmacklem				if (dp != NULL) {
3250244042Srmacklem					if (NFSISSET_ATTRBIT(&attrbits,
3251244042Srmacklem					    NFSATTRBIT_SIZE)) {
3252244042Srmacklem						if (vp != NULL)
3253244042Srmacklem							va.va_size = np->n_size;
3254244042Srmacklem						else
3255244042Srmacklem							va.va_size =
3256244042Srmacklem							    dp->nfsdl_size;
3257244042Srmacklem						NFSSETBIT_ATTRBIT(&rattrbits,
3258244042Srmacklem						    NFSATTRBIT_SIZE);
3259244042Srmacklem					}
3260244042Srmacklem					if (NFSISSET_ATTRBIT(&attrbits,
3261244042Srmacklem					    NFSATTRBIT_CHANGE)) {
3262244042Srmacklem						va.va_filerev =
3263244042Srmacklem						    dp->nfsdl_change;
3264244042Srmacklem						if (vp == NULL ||
3265244042Srmacklem						    (np->n_flag & NDELEGMOD))
3266244042Srmacklem							va.va_filerev++;
3267244042Srmacklem						NFSSETBIT_ATTRBIT(&rattrbits,
3268244042Srmacklem						    NFSATTRBIT_CHANGE);
3269244042Srmacklem					}
3270244042Srmacklem				} else
3271244042Srmacklem					error = NFSERR_SERVERFAULT;
3272191783Srmacklem				NFSUNLOCKCLSTATE();
3273191783Srmacklem			}
3274244042Srmacklem			if (vp != NULL)
3275244042Srmacklem				vrele(vp);
3276244042Srmacklem			if (mp != NULL)
3277244042Srmacklem				vfs_unbusy(mp);
3278191783Srmacklem			if (nfhp != NULL)
3279191783Srmacklem				FREE((caddr_t)nfhp, M_NFSFH);
3280244042Srmacklem			if (!error)
3281220645Srmacklem				(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
3282220648Srmacklem				    NULL, 0, &rattrbits, NULL, NULL, 0, 0, 0, 0,
3283220645Srmacklem				    (uint64_t)0);
3284191783Srmacklem			break;
3285191783Srmacklem		case NFSV4OP_CBRECALL:
3286244042Srmacklem			NFSCL_DEBUG(4, "cbrecall\n");
3287191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
3288191783Srmacklem			    NFSX_UNSIGNED);
3289191783Srmacklem			stateid.seqid = *tl++;
3290191783Srmacklem			NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
3291191783Srmacklem			    NFSX_STATEIDOTHER);
3292191783Srmacklem			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3293191783Srmacklem			trunc = fxdr_unsigned(int, *tl);
3294191783Srmacklem			error = nfsm_getfh(nd, &nfhp);
3295244042Srmacklem			if (error == 0 && i == 0 &&
3296244042Srmacklem			    minorvers != NFSV4_MINORVERSION)
3297244042Srmacklem				error = NFSERR_OPNOTINSESS;
3298191783Srmacklem			if (!error) {
3299191783Srmacklem				NFSLOCKCLSTATE();
3300244042Srmacklem				if (minorvers == NFSV4_MINORVERSION)
3301244042Srmacklem					clp = nfscl_getclnt(cbident);
3302244042Srmacklem				else
3303244042Srmacklem					clp = nfscl_getclntsess(sessionid);
3304191783Srmacklem				if (clp != NULL) {
3305191783Srmacklem					dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3306191783Srmacklem					    nfhp->nfh_len);
3307214406Srmacklem					if (dp != NULL && (dp->nfsdl_flags &
3308214406Srmacklem					    NFSCLDL_DELEGRET) == 0) {
3309191783Srmacklem						dp->nfsdl_flags |=
3310191783Srmacklem						    NFSCLDL_RECALL;
3311191783Srmacklem						wakeup((caddr_t)clp);
3312191783Srmacklem					}
3313191783Srmacklem				} else {
3314191783Srmacklem					error = NFSERR_SERVERFAULT;
3315191783Srmacklem				}
3316191783Srmacklem				NFSUNLOCKCLSTATE();
3317191783Srmacklem			}
3318191783Srmacklem			if (nfhp != NULL)
3319191783Srmacklem				FREE((caddr_t)nfhp, M_NFSFH);
3320191783Srmacklem			break;
3321244042Srmacklem		case NFSV4OP_CBLAYOUTRECALL:
3322244042Srmacklem			NFSCL_DEBUG(4, "cblayrec\n");
3323244042Srmacklem			nfhp = NULL;
3324244042Srmacklem			NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
3325244042Srmacklem			laytype = fxdr_unsigned(int, *tl++);
3326244042Srmacklem			iomode = fxdr_unsigned(uint32_t, *tl++);
3327244042Srmacklem			if (newnfs_true == *tl++)
3328244042Srmacklem				changed = 1;
3329244042Srmacklem			else
3330244042Srmacklem				changed = 0;
3331244042Srmacklem			recalltype = fxdr_unsigned(int, *tl);
3332244042Srmacklem			recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL,
3333244042Srmacklem			    M_WAITOK);
3334244042Srmacklem			if (laytype != NFSLAYOUT_NFSV4_1_FILES)
3335244042Srmacklem				error = NFSERR_NOMATCHLAYOUT;
3336244042Srmacklem			else if (recalltype == NFSLAYOUTRETURN_FILE) {
3337244042Srmacklem				error = nfsm_getfh(nd, &nfhp);
3338244042Srmacklem				NFSCL_DEBUG(4, "retfile getfh=%d\n", error);
3339244042Srmacklem				if (error != 0)
3340244042Srmacklem					goto nfsmout;
3341244042Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER +
3342244042Srmacklem				    NFSX_STATEID);
3343244042Srmacklem				off = fxdr_hyper(tl); tl += 2;
3344244042Srmacklem				len = fxdr_hyper(tl); tl += 2;
3345244042Srmacklem				stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
3346244042Srmacklem				NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
3347244042Srmacklem				if (minorvers == NFSV4_MINORVERSION)
3348244042Srmacklem					error = NFSERR_NOTSUPP;
3349244042Srmacklem				else if (i == 0)
3350244042Srmacklem					error = NFSERR_OPNOTINSESS;
3351244042Srmacklem				if (error == 0) {
3352244042Srmacklem					NFSLOCKCLSTATE();
3353244042Srmacklem					clp = nfscl_getclntsess(sessionid);
3354244042Srmacklem					NFSCL_DEBUG(4, "cbly clp=%p\n", clp);
3355244042Srmacklem					if (clp != NULL) {
3356244042Srmacklem						lyp = nfscl_findlayout(clp,
3357244042Srmacklem						    nfhp->nfh_fh,
3358244042Srmacklem						    nfhp->nfh_len);
3359244042Srmacklem						NFSCL_DEBUG(4, "cblyp=%p\n",
3360244042Srmacklem						    lyp);
3361244042Srmacklem						if (lyp != NULL &&
3362244042Srmacklem						    (lyp->nfsly_flags &
3363244042Srmacklem						     NFSLY_FILES) != 0 &&
3364244042Srmacklem						    !NFSBCMP(stateid.other,
3365244042Srmacklem						    lyp->nfsly_stateid.other,
3366244042Srmacklem						    NFSX_STATEIDOTHER)) {
3367244042Srmacklem							error =
3368244042Srmacklem							    nfscl_layoutrecall(
3369244042Srmacklem							    recalltype,
3370244042Srmacklem							    lyp, iomode, off,
3371244042Srmacklem							    len, stateid.seqid,
3372244042Srmacklem							    recallp);
3373244042Srmacklem							recallp = NULL;
3374244042Srmacklem							wakeup(clp);
3375244042Srmacklem							NFSCL_DEBUG(4,
3376244042Srmacklem							    "aft layrcal=%d\n",
3377244042Srmacklem							    error);
3378244042Srmacklem						} else
3379244042Srmacklem							error =
3380244042Srmacklem							  NFSERR_NOMATCHLAYOUT;
3381244042Srmacklem					} else
3382244042Srmacklem						error = NFSERR_NOMATCHLAYOUT;
3383244042Srmacklem					NFSUNLOCKCLSTATE();
3384244042Srmacklem				}
3385244042Srmacklem				free(nfhp, M_NFSFH);
3386244042Srmacklem			} else if (recalltype == NFSLAYOUTRETURN_FSID) {
3387244042Srmacklem				NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER);
3388244042Srmacklem				filesid[0] = fxdr_hyper(tl); tl += 2;
3389244042Srmacklem				filesid[1] = fxdr_hyper(tl); tl += 2;
3390244042Srmacklem				gotone = 0;
3391244042Srmacklem				NFSLOCKCLSTATE();
3392244042Srmacklem				clp = nfscl_getclntsess(sessionid);
3393244042Srmacklem				if (clp != NULL) {
3394244042Srmacklem					TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3395244042Srmacklem					    nfsly_list) {
3396244042Srmacklem						if (lyp->nfsly_filesid[0] ==
3397244042Srmacklem						    filesid[0] &&
3398244042Srmacklem						    lyp->nfsly_filesid[1] ==
3399244042Srmacklem						    filesid[1]) {
3400244042Srmacklem							error =
3401244042Srmacklem							    nfscl_layoutrecall(
3402244042Srmacklem							    recalltype,
3403244042Srmacklem							    lyp, iomode, 0,
3404244042Srmacklem							    UINT64_MAX,
3405244042Srmacklem							    lyp->nfsly_stateid.seqid,
3406244042Srmacklem							    recallp);
3407244042Srmacklem							recallp = NULL;
3408244042Srmacklem							gotone = 1;
3409244042Srmacklem						}
3410244042Srmacklem					}
3411244042Srmacklem					if (gotone != 0)
3412244042Srmacklem						wakeup(clp);
3413244042Srmacklem					else
3414244042Srmacklem						error = NFSERR_NOMATCHLAYOUT;
3415244042Srmacklem				} else
3416244042Srmacklem					error = NFSERR_NOMATCHLAYOUT;
3417244042Srmacklem				NFSUNLOCKCLSTATE();
3418244042Srmacklem			} else if (recalltype == NFSLAYOUTRETURN_ALL) {
3419244042Srmacklem				gotone = 0;
3420244042Srmacklem				NFSLOCKCLSTATE();
3421244042Srmacklem				clp = nfscl_getclntsess(sessionid);
3422244042Srmacklem				if (clp != NULL) {
3423244042Srmacklem					TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3424244042Srmacklem					    nfsly_list) {
3425244042Srmacklem						error = nfscl_layoutrecall(
3426244042Srmacklem						    recalltype, lyp, iomode, 0,
3427244042Srmacklem						    UINT64_MAX,
3428244042Srmacklem						    lyp->nfsly_stateid.seqid,
3429244042Srmacklem						    recallp);
3430244042Srmacklem						recallp = NULL;
3431244042Srmacklem						gotone = 1;
3432244042Srmacklem					}
3433244042Srmacklem					if (gotone != 0)
3434244042Srmacklem						wakeup(clp);
3435244042Srmacklem					else
3436244042Srmacklem						error = NFSERR_NOMATCHLAYOUT;
3437244042Srmacklem				} else
3438244042Srmacklem					error = NFSERR_NOMATCHLAYOUT;
3439244042Srmacklem				NFSUNLOCKCLSTATE();
3440244042Srmacklem			} else
3441244042Srmacklem				error = NFSERR_NOMATCHLAYOUT;
3442244042Srmacklem			if (recallp != NULL) {
3443244042Srmacklem				free(recallp, M_NFSLAYRECALL);
3444244042Srmacklem				recallp = NULL;
3445244042Srmacklem			}
3446244042Srmacklem			break;
3447244042Srmacklem		case NFSV4OP_CBSEQUENCE:
3448244042Srmacklem			NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3449244042Srmacklem			    5 * NFSX_UNSIGNED);
3450244042Srmacklem			bcopy(tl, sessionid, NFSX_V4SESSIONID);
3451244042Srmacklem			tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3452244042Srmacklem			seqid = fxdr_unsigned(uint32_t, *tl++);
3453244042Srmacklem			slotid = fxdr_unsigned(uint32_t, *tl++);
3454244042Srmacklem			highslot = fxdr_unsigned(uint32_t, *tl++);
3455244042Srmacklem			cachethis = *tl++;
3456244042Srmacklem			/* Throw away the referring call stuff. */
3457244042Srmacklem			clist = fxdr_unsigned(int, *tl);
3458244042Srmacklem			for (j = 0; j < clist; j++) {
3459244042Srmacklem				NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3460244042Srmacklem				    NFSX_UNSIGNED);
3461244042Srmacklem				tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3462244042Srmacklem				rcalls = fxdr_unsigned(int, *tl);
3463244042Srmacklem				for (k = 0; k < rcalls; k++) {
3464244042Srmacklem					NFSM_DISSECT(tl, uint32_t *,
3465244042Srmacklem					    2 * NFSX_UNSIGNED);
3466244042Srmacklem				}
3467244042Srmacklem			}
3468244042Srmacklem			NFSLOCKCLSTATE();
3469244042Srmacklem			if (i == 0) {
3470244042Srmacklem				clp = nfscl_getclntsess(sessionid);
3471244042Srmacklem				if (clp == NULL)
3472244042Srmacklem					error = NFSERR_SERVERFAULT;
3473244042Srmacklem			} else
3474244042Srmacklem				error = NFSERR_SEQUENCEPOS;
3475244042Srmacklem			if (error == 0)
3476244042Srmacklem				error = nfsv4_seqsession(seqid, slotid,
3477244042Srmacklem				    highslot,
3478244042Srmacklem				    NFSMNT_MDSSESSION(clp->nfsc_nmp)->
3479244042Srmacklem				    nfsess_cbslots, &rep,
3480244042Srmacklem				    NFSMNT_MDSSESSION(clp->nfsc_nmp)->
3481244042Srmacklem				    nfsess_backslots);
3482244042Srmacklem			NFSUNLOCKCLSTATE();
3483244042Srmacklem			if (error == 0) {
3484244042Srmacklem				gotseq_ok = 1;
3485244042Srmacklem				if (rep != NULL) {
3486244042Srmacklem					NFSCL_DEBUG(4, "Got cbretry\n");
3487244042Srmacklem					m_freem(nd->nd_mreq);
3488244042Srmacklem					nd->nd_mreq = rep;
3489244042Srmacklem					rep = NULL;
3490244042Srmacklem					goto out;
3491244042Srmacklem				}
3492244042Srmacklem				NFSM_BUILD(tl, uint32_t *,
3493244042Srmacklem				    NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
3494244042Srmacklem				bcopy(sessionid, tl, NFSX_V4SESSIONID);
3495244042Srmacklem				tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3496244042Srmacklem				*tl++ = txdr_unsigned(seqid);
3497244042Srmacklem				*tl++ = txdr_unsigned(slotid);
3498244042Srmacklem				*tl++ = txdr_unsigned(NFSV4_CBSLOTS - 1);
3499244042Srmacklem				*tl = txdr_unsigned(NFSV4_CBSLOTS - 1);
3500244042Srmacklem			}
3501244042Srmacklem			break;
3502244042Srmacklem		default:
3503244042Srmacklem			if (i == 0 && minorvers == NFSV41_MINORVERSION)
3504244042Srmacklem				error = NFSERR_OPNOTINSESS;
3505244042Srmacklem			else {
3506244042Srmacklem				NFSCL_DEBUG(1, "unsupp callback %d\n", op);
3507244042Srmacklem				error = NFSERR_NOTSUPP;
3508244042Srmacklem			}
3509244042Srmacklem			break;
3510191783Srmacklem		};
3511191783Srmacklem		if (error) {
3512191783Srmacklem			if (error == EBADRPC || error == NFSERR_BADXDR) {
3513191783Srmacklem				nd->nd_repstat = NFSERR_BADXDR;
3514191783Srmacklem			} else {
3515191783Srmacklem				nd->nd_repstat = error;
3516191783Srmacklem			}
3517191783Srmacklem			error = 0;
3518191783Srmacklem		}
3519191783Srmacklem		retops++;
3520191783Srmacklem		if (nd->nd_repstat) {
3521191783Srmacklem			*repp = nfscl_errmap(nd);
3522191783Srmacklem			break;
3523191783Srmacklem		} else
3524191783Srmacklem			*repp = 0;	/* NFS4_OK */
3525191783Srmacklem	}
3526191783Srmacklemnfsmout:
3527244042Srmacklem	if (recallp != NULL)
3528244042Srmacklem		free(recallp, M_NFSLAYRECALL);
3529191783Srmacklem	if (error) {
3530191783Srmacklem		if (error == EBADRPC || error == NFSERR_BADXDR)
3531191783Srmacklem			nd->nd_repstat = NFSERR_BADXDR;
3532191783Srmacklem		else
3533191783Srmacklem			printf("nfsv4 comperr1=%d\n", error);
3534191783Srmacklem	}
3535191783Srmacklem	if (taglen == -1) {
3536191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3537191783Srmacklem		*tl++ = 0;
3538191783Srmacklem		*tl = 0;
3539191783Srmacklem	} else {
3540191783Srmacklem		*retopsp = txdr_unsigned(retops);
3541191783Srmacklem	}
3542191783Srmacklem	*nd->nd_errp = nfscl_errmap(nd);
3543244042Srmacklemout:
3544244042Srmacklem	if (gotseq_ok != 0) {
3545244042Srmacklem		rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK);
3546244042Srmacklem		NFSLOCKCLSTATE();
3547244042Srmacklem		clp = nfscl_getclntsess(sessionid);
3548244042Srmacklem		if (clp != NULL) {
3549244042Srmacklem			nfsv4_seqsess_cacherep(slotid,
3550244042Srmacklem			    NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_cbslots,
3551244042Srmacklem			    rep);
3552244042Srmacklem			NFSUNLOCKCLSTATE();
3553244042Srmacklem		} else {
3554244042Srmacklem			NFSUNLOCKCLSTATE();
3555244042Srmacklem			m_freem(rep);
3556244042Srmacklem		}
3557244042Srmacklem	}
3558191783Srmacklem}
3559191783Srmacklem
3560191783Srmacklem/*
3561191783Srmacklem * Generate the next cbident value. Basically just increment a static value
3562191783Srmacklem * and then check that it isn't already in the list, if it has wrapped around.
3563191783Srmacklem */
3564191783Srmacklemstatic u_int32_t
3565191783Srmacklemnfscl_nextcbident(void)
3566191783Srmacklem{
3567191783Srmacklem	struct nfsclclient *clp;
3568191783Srmacklem	int matched;
3569191783Srmacklem	static u_int32_t nextcbident = 0;
3570191783Srmacklem	static int haswrapped = 0;
3571191783Srmacklem
3572191783Srmacklem	nextcbident++;
3573191783Srmacklem	if (nextcbident == 0)
3574191783Srmacklem		haswrapped = 1;
3575191783Srmacklem	if (haswrapped) {
3576191783Srmacklem		/*
3577191783Srmacklem		 * Search the clientid list for one already using this cbident.
3578191783Srmacklem		 */
3579191783Srmacklem		do {
3580191783Srmacklem			matched = 0;
3581191783Srmacklem			NFSLOCKCLSTATE();
3582191783Srmacklem			LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3583191783Srmacklem				if (clp->nfsc_cbident == nextcbident) {
3584191783Srmacklem					matched = 1;
3585191783Srmacklem					break;
3586191783Srmacklem				}
3587191783Srmacklem			}
3588191783Srmacklem			NFSUNLOCKCLSTATE();
3589191783Srmacklem			if (matched == 1)
3590191783Srmacklem				nextcbident++;
3591191783Srmacklem		} while (matched);
3592191783Srmacklem	}
3593191783Srmacklem	return (nextcbident);
3594191783Srmacklem}
3595191783Srmacklem
3596191783Srmacklem/*
3597244042Srmacklem * Get the mount point related to a given cbident or session and busy it.
3598191783Srmacklem */
3599191783Srmacklemstatic mount_t
3600244042Srmacklemnfscl_getmnt(int minorvers, uint8_t *sessionid, u_int32_t cbident,
3601244042Srmacklem    struct nfsclclient **clpp)
3602191783Srmacklem{
3603191783Srmacklem	struct nfsclclient *clp;
3604244042Srmacklem	mount_t mp;
3605244042Srmacklem	int error;
3606191783Srmacklem
3607244042Srmacklem	*clpp = NULL;
3608191783Srmacklem	NFSLOCKCLSTATE();
3609191783Srmacklem	LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3610244042Srmacklem		if (minorvers == NFSV4_MINORVERSION) {
3611244042Srmacklem			if (clp->nfsc_cbident == cbident)
3612244042Srmacklem				break;
3613244042Srmacklem		} else if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)->
3614244042Srmacklem		    nfsess_sessionid, sessionid, NFSX_V4SESSIONID))
3615191783Srmacklem			break;
3616191783Srmacklem	}
3617191783Srmacklem	if (clp == NULL) {
3618191783Srmacklem		NFSUNLOCKCLSTATE();
3619191783Srmacklem		return (NULL);
3620191783Srmacklem	}
3621244042Srmacklem	mp = clp->nfsc_nmp->nm_mountp;
3622244042Srmacklem	vfs_ref(mp);
3623191783Srmacklem	NFSUNLOCKCLSTATE();
3624244042Srmacklem	error = vfs_busy(mp, 0);
3625244042Srmacklem	vfs_rel(mp);
3626244042Srmacklem	if (error != 0)
3627244042Srmacklem		return (NULL);
3628244042Srmacklem	*clpp = clp;
3629244042Srmacklem	return (mp);
3630191783Srmacklem}
3631191783Srmacklem
3632191783Srmacklem/*
3633244042Srmacklem * Get the clientid pointer related to a given cbident.
3634244042Srmacklem */
3635244042Srmacklemstatic struct nfsclclient *
3636244042Srmacklemnfscl_getclnt(u_int32_t cbident)
3637244042Srmacklem{
3638244042Srmacklem	struct nfsclclient *clp;
3639244042Srmacklem
3640244042Srmacklem	LIST_FOREACH(clp, &nfsclhead, nfsc_list)
3641244042Srmacklem		if (clp->nfsc_cbident == cbident)
3642244042Srmacklem			break;
3643244042Srmacklem	return (clp);
3644244042Srmacklem}
3645244042Srmacklem
3646244042Srmacklem/*
3647244042Srmacklem * Get the clientid pointer related to a given sessionid.
3648244042Srmacklem */
3649244042Srmacklemstatic struct nfsclclient *
3650244042Srmacklemnfscl_getclntsess(uint8_t *sessionid)
3651244042Srmacklem{
3652244042Srmacklem	struct nfsclclient *clp;
3653244042Srmacklem
3654244042Srmacklem	LIST_FOREACH(clp, &nfsclhead, nfsc_list)
3655244042Srmacklem		if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_sessionid,
3656244042Srmacklem		    sessionid, NFSX_V4SESSIONID))
3657244042Srmacklem			break;
3658244042Srmacklem	return (clp);
3659244042Srmacklem}
3660244042Srmacklem
3661244042Srmacklem/*
3662191783Srmacklem * Search for a lock conflict locally on the client. A conflict occurs if
3663191783Srmacklem * - not same owner and overlapping byte range and at least one of them is
3664191783Srmacklem *   a write lock or this is an unlock.
3665191783Srmacklem */
3666191783Srmacklemstatic int
3667201439Srmacklemnfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
3668201439Srmacklem    struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
3669201439Srmacklem    struct nfscllock **lopp)
3670191783Srmacklem{
3671191783Srmacklem	struct nfsclowner *owp;
3672191783Srmacklem	struct nfsclopen *op;
3673191783Srmacklem	int ret;
3674191783Srmacklem
3675191783Srmacklem	if (dp != NULL) {
3676191783Srmacklem		ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
3677191783Srmacklem		if (ret)
3678191783Srmacklem			return (ret);
3679191783Srmacklem	}
3680191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3681191783Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3682201439Srmacklem			if (op->nfso_fhlen == fhlen &&
3683201439Srmacklem			    !NFSBCMP(op->nfso_fh, fhp, fhlen)) {
3684201439Srmacklem				ret = nfscl_checkconflict(&op->nfso_lock, nlop,
3685201439Srmacklem				    own, lopp);
3686201439Srmacklem				if (ret)
3687201439Srmacklem					return (ret);
3688201439Srmacklem			}
3689191783Srmacklem		}
3690191783Srmacklem	}
3691191783Srmacklem	return (0);
3692191783Srmacklem}
3693191783Srmacklem
3694191783Srmacklemstatic int
3695191783Srmacklemnfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
3696191783Srmacklem    u_int8_t *own, struct nfscllock **lopp)
3697191783Srmacklem{
3698191783Srmacklem	struct nfscllockowner *lp;
3699191783Srmacklem	struct nfscllock *lop;
3700191783Srmacklem
3701191783Srmacklem	LIST_FOREACH(lp, lhp, nfsl_list) {
3702191783Srmacklem		if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
3703191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3704191783Srmacklem				if (lop->nfslo_first >= nlop->nfslo_end)
3705191783Srmacklem					break;
3706191783Srmacklem				if (lop->nfslo_end <= nlop->nfslo_first)
3707191783Srmacklem					continue;
3708191783Srmacklem				if (lop->nfslo_type == F_WRLCK ||
3709191783Srmacklem				    nlop->nfslo_type == F_WRLCK ||
3710191783Srmacklem				    nlop->nfslo_type == F_UNLCK) {
3711191783Srmacklem					if (lopp != NULL)
3712191783Srmacklem						*lopp = lop;
3713191783Srmacklem					return (NFSERR_DENIED);
3714191783Srmacklem				}
3715191783Srmacklem			}
3716191783Srmacklem		}
3717191783Srmacklem	}
3718191783Srmacklem	return (0);
3719191783Srmacklem}
3720191783Srmacklem
3721191783Srmacklem/*
3722191783Srmacklem * Check for a local conflicting lock.
3723191783Srmacklem */
3724191783SrmacklemAPPLESTATIC int
3725191783Srmacklemnfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
3726222719Srmacklem    u_int64_t len, struct flock *fl, NFSPROC_T *p, void *id, int flags)
3727191783Srmacklem{
3728191783Srmacklem	struct nfscllock *lop, nlck;
3729191783Srmacklem	struct nfscldeleg *dp;
3730191783Srmacklem	struct nfsnode *np;
3731191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
3732191783Srmacklem	int error;
3733191783Srmacklem
3734191783Srmacklem	nlck.nfslo_type = fl->l_type;
3735191783Srmacklem	nlck.nfslo_first = off;
3736191783Srmacklem	if (len == NFS64BITSSET) {
3737191783Srmacklem		nlck.nfslo_end = NFS64BITSSET;
3738191783Srmacklem	} else {
3739191783Srmacklem		nlck.nfslo_end = off + len;
3740191783Srmacklem		if (nlck.nfslo_end <= nlck.nfslo_first)
3741191783Srmacklem			return (NFSERR_INVAL);
3742191783Srmacklem	}
3743191783Srmacklem	np = VTONFS(vp);
3744222719Srmacklem	nfscl_filllockowner(id, own, flags);
3745191783Srmacklem	NFSLOCKCLSTATE();
3746191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3747201439Srmacklem	error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
3748201439Srmacklem	    &nlck, own, dp, &lop);
3749201439Srmacklem	if (error != 0) {
3750191783Srmacklem		fl->l_whence = SEEK_SET;
3751191783Srmacklem		fl->l_start = lop->nfslo_first;
3752191783Srmacklem		if (lop->nfslo_end == NFS64BITSSET)
3753191783Srmacklem			fl->l_len = 0;
3754191783Srmacklem		else
3755191783Srmacklem			fl->l_len = lop->nfslo_end - lop->nfslo_first;
3756191783Srmacklem		fl->l_pid = (pid_t)0;
3757191783Srmacklem		fl->l_type = lop->nfslo_type;
3758201439Srmacklem		error = -1;			/* no RPC required */
3759191783Srmacklem	} else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
3760191783Srmacklem	    fl->l_type == F_RDLCK)) {
3761191783Srmacklem		/*
3762191783Srmacklem		 * The delegation ensures that there isn't a conflicting
3763191783Srmacklem		 * lock on the server, so return -1 to indicate an RPC
3764191783Srmacklem		 * isn't required.
3765191783Srmacklem		 */
3766191783Srmacklem		fl->l_type = F_UNLCK;
3767191783Srmacklem		error = -1;
3768191783Srmacklem	}
3769191783Srmacklem	NFSUNLOCKCLSTATE();
3770191783Srmacklem	return (error);
3771191783Srmacklem}
3772191783Srmacklem
3773191783Srmacklem/*
3774191783Srmacklem * Handle Recall of a delegation.
3775191783Srmacklem * The clp must be exclusive locked when this is called.
3776191783Srmacklem */
3777191783Srmacklemstatic int
3778191783Srmacklemnfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
3779207082Srmacklem    struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p,
3780207082Srmacklem    int called_from_renewthread)
3781191783Srmacklem{
3782191783Srmacklem	struct nfsclowner *owp, *lowp, *nowp;
3783191783Srmacklem	struct nfsclopen *op, *lop;
3784191783Srmacklem	struct nfscllockowner *lp;
3785191783Srmacklem	struct nfscllock *lckp;
3786191783Srmacklem	struct nfsnode *np;
3787191783Srmacklem	int error = 0, ret, gotvp = 0;
3788191783Srmacklem
3789191783Srmacklem	if (vp == NULL) {
3790191783Srmacklem		/*
3791191783Srmacklem		 * First, get a vnode for the file. This is needed to do RPCs.
3792191783Srmacklem		 */
3793191783Srmacklem		ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
3794191783Srmacklem		    dp->nfsdl_fhlen, p, &np);
3795191783Srmacklem		if (ret) {
3796191783Srmacklem			/*
3797191783Srmacklem			 * File isn't open, so nothing to move over to the
3798191783Srmacklem			 * server.
3799191783Srmacklem			 */
3800191783Srmacklem			return (0);
3801191783Srmacklem		}
3802191783Srmacklem		vp = NFSTOV(np);
3803191783Srmacklem		gotvp = 1;
3804191783Srmacklem	} else {
3805191783Srmacklem		np = VTONFS(vp);
3806191783Srmacklem	}
3807191783Srmacklem	dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
3808191783Srmacklem
3809191783Srmacklem	/*
3810191783Srmacklem	 * Ok, if it's a write delegation, flush data to the server, so
3811191783Srmacklem	 * that close/open consistency is retained.
3812191783Srmacklem	 */
3813207082Srmacklem	ret = 0;
3814191783Srmacklem	NFSLOCKNODE(np);
3815191783Srmacklem	if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
3816191783Srmacklem		np->n_flag |= NDELEGRECALL;
3817191783Srmacklem		NFSUNLOCKNODE(np);
3818207082Srmacklem		ret = ncl_flush(vp, MNT_WAIT, cred, p, 1,
3819207082Srmacklem		    called_from_renewthread);
3820191783Srmacklem		NFSLOCKNODE(np);
3821214406Srmacklem		np->n_flag &= ~NDELEGRECALL;
3822191783Srmacklem	}
3823214406Srmacklem	NFSINVALATTRCACHE(np);
3824191783Srmacklem	NFSUNLOCKNODE(np);
3825207082Srmacklem	if (ret == EIO && called_from_renewthread != 0) {
3826207082Srmacklem		/*
3827207082Srmacklem		 * If the flush failed with EIO for the renew thread,
3828207082Srmacklem		 * return now, so that the dirty buffer will be flushed
3829207082Srmacklem		 * later.
3830207082Srmacklem		 */
3831207082Srmacklem		if (gotvp != 0)
3832207082Srmacklem			vrele(vp);
3833207082Srmacklem		return (ret);
3834207082Srmacklem	}
3835191783Srmacklem
3836191783Srmacklem	/*
3837191783Srmacklem	 * Now, for each openowner with opens issued locally, move them
3838191783Srmacklem	 * over to state against the server.
3839191783Srmacklem	 */
3840191783Srmacklem	LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
3841191783Srmacklem		lop = LIST_FIRST(&lowp->nfsow_open);
3842191783Srmacklem		if (lop != NULL) {
3843191783Srmacklem			if (LIST_NEXT(lop, nfso_list) != NULL)
3844191783Srmacklem				panic("nfsdlg mult opens");
3845191783Srmacklem			/*
3846191783Srmacklem			 * Look for the same openowner against the server.
3847191783Srmacklem			 */
3848191783Srmacklem			LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3849191783Srmacklem				if (!NFSBCMP(lowp->nfsow_owner,
3850191783Srmacklem				    owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
3851191783Srmacklem					newnfs_copycred(&dp->nfsdl_cred, cred);
3852191783Srmacklem					ret = nfscl_moveopen(vp, clp, nmp, lop,
3853191783Srmacklem					    owp, dp, cred, p);
3854191783Srmacklem					if (ret == NFSERR_STALECLIENTID ||
3855244042Srmacklem					    ret == NFSERR_STALEDONTRECOVER ||
3856244042Srmacklem					    ret == NFSERR_BADSESSION) {
3857191783Srmacklem						if (gotvp)
3858191783Srmacklem							vrele(vp);
3859191783Srmacklem						return (ret);
3860191783Srmacklem					}
3861191783Srmacklem					if (ret) {
3862191783Srmacklem						nfscl_freeopen(lop, 1);
3863191783Srmacklem						if (!error)
3864191783Srmacklem							error = ret;
3865191783Srmacklem					}
3866191783Srmacklem					break;
3867191783Srmacklem				}
3868191783Srmacklem			}
3869191783Srmacklem
3870191783Srmacklem			/*
3871191783Srmacklem			 * If no openowner found, create one and get an open
3872191783Srmacklem			 * for it.
3873191783Srmacklem			 */
3874191783Srmacklem			if (owp == NULL) {
3875191783Srmacklem				MALLOC(nowp, struct nfsclowner *,
3876191783Srmacklem				    sizeof (struct nfsclowner), M_NFSCLOWNER,
3877191783Srmacklem				    M_WAITOK);
3878191783Srmacklem				nfscl_newopen(clp, NULL, &owp, &nowp, &op,
3879191783Srmacklem				    NULL, lowp->nfsow_owner, dp->nfsdl_fh,
3880191783Srmacklem				    dp->nfsdl_fhlen, NULL);
3881191783Srmacklem				newnfs_copycred(&dp->nfsdl_cred, cred);
3882191783Srmacklem				ret = nfscl_moveopen(vp, clp, nmp, lop,
3883191783Srmacklem				    owp, dp, cred, p);
3884191783Srmacklem				if (ret) {
3885191783Srmacklem					nfscl_freeopenowner(owp, 0);
3886191783Srmacklem					if (ret == NFSERR_STALECLIENTID ||
3887244042Srmacklem					    ret == NFSERR_STALEDONTRECOVER ||
3888244042Srmacklem					    ret == NFSERR_BADSESSION) {
3889191783Srmacklem						if (gotvp)
3890191783Srmacklem							vrele(vp);
3891191783Srmacklem						return (ret);
3892191783Srmacklem					}
3893191783Srmacklem					if (ret) {
3894191783Srmacklem						nfscl_freeopen(lop, 1);
3895191783Srmacklem						if (!error)
3896191783Srmacklem							error = ret;
3897191783Srmacklem					}
3898191783Srmacklem				}
3899191783Srmacklem			}
3900191783Srmacklem		}
3901191783Srmacklem	}
3902191783Srmacklem
3903191783Srmacklem	/*
3904191783Srmacklem	 * Now, get byte range locks for any locks done locally.
3905191783Srmacklem	 */
3906191783Srmacklem	LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3907191783Srmacklem		LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
3908191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
3909191783Srmacklem			ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
3910191783Srmacklem			if (ret == NFSERR_STALESTATEID ||
3911191783Srmacklem			    ret == NFSERR_STALEDONTRECOVER ||
3912244042Srmacklem			    ret == NFSERR_STALECLIENTID ||
3913244042Srmacklem			    ret == NFSERR_BADSESSION) {
3914191783Srmacklem				if (gotvp)
3915191783Srmacklem					vrele(vp);
3916191783Srmacklem				return (ret);
3917191783Srmacklem			}
3918191783Srmacklem			if (ret && !error)
3919191783Srmacklem				error = ret;
3920191783Srmacklem		}
3921191783Srmacklem	}
3922191783Srmacklem	if (gotvp)
3923191783Srmacklem		vrele(vp);
3924191783Srmacklem	return (error);
3925191783Srmacklem}
3926191783Srmacklem
3927191783Srmacklem/*
3928191783Srmacklem * Move a locally issued open over to an owner on the state list.
3929191783Srmacklem * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
3930191783Srmacklem * returns with it unlocked.
3931191783Srmacklem */
3932191783Srmacklemstatic int
3933191783Srmacklemnfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3934191783Srmacklem    struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
3935191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
3936191783Srmacklem{
3937191783Srmacklem	struct nfsclopen *op, *nop;
3938191783Srmacklem	struct nfscldeleg *ndp;
3939191783Srmacklem	struct nfsnode *np;
3940191783Srmacklem	int error = 0, newone;
3941191783Srmacklem
3942191783Srmacklem	/*
3943191783Srmacklem	 * First, look for an appropriate open, If found, just increment the
3944191783Srmacklem	 * opencnt in it.
3945191783Srmacklem	 */
3946191783Srmacklem	LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3947191783Srmacklem		if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
3948191783Srmacklem		    op->nfso_fhlen == lop->nfso_fhlen &&
3949191783Srmacklem		    !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
3950191783Srmacklem			op->nfso_opencnt += lop->nfso_opencnt;
3951191783Srmacklem			nfscl_freeopen(lop, 1);
3952191783Srmacklem			return (0);
3953191783Srmacklem		}
3954191783Srmacklem	}
3955191783Srmacklem
3956191783Srmacklem	/* No appropriate open, so we have to do one against the server. */
3957191783Srmacklem	np = VTONFS(vp);
3958191783Srmacklem	MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
3959191783Srmacklem	    lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
3960191783Srmacklem	newone = 0;
3961191783Srmacklem	nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
3962191783Srmacklem	    lop->nfso_fh, lop->nfso_fhlen, &newone);
3963191783Srmacklem	ndp = dp;
3964191783Srmacklem	error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen,
3965191783Srmacklem	    lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
3966191783Srmacklem	    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
3967191783Srmacklem	if (error) {
3968191783Srmacklem		if (newone)
3969191783Srmacklem			nfscl_freeopen(op, 0);
3970191783Srmacklem	} else {
3971191783Srmacklem		if (newone)
3972191783Srmacklem			newnfs_copyincred(cred, &op->nfso_cred);
3973191783Srmacklem		op->nfso_mode |= lop->nfso_mode;
3974191783Srmacklem		op->nfso_opencnt += lop->nfso_opencnt;
3975191783Srmacklem		nfscl_freeopen(lop, 1);
3976191783Srmacklem	}
3977191783Srmacklem	if (nop != NULL)
3978191783Srmacklem		FREE((caddr_t)nop, M_NFSCLOPEN);
3979191783Srmacklem	if (ndp != NULL) {
3980191783Srmacklem		/*
3981191783Srmacklem		 * What should I do with the returned delegation, since the
3982191783Srmacklem		 * delegation is being recalled? For now, just printf and
3983191783Srmacklem		 * through it away.
3984191783Srmacklem		 */
3985191783Srmacklem		printf("Moveopen returned deleg\n");
3986191783Srmacklem		FREE((caddr_t)ndp, M_NFSCLDELEG);
3987191783Srmacklem	}
3988191783Srmacklem	return (error);
3989191783Srmacklem}
3990191783Srmacklem
3991191783Srmacklem/*
3992191783Srmacklem * Recall all delegations on this client.
3993191783Srmacklem */
3994191783Srmacklemstatic void
3995191783Srmacklemnfscl_totalrecall(struct nfsclclient *clp)
3996191783Srmacklem{
3997191783Srmacklem	struct nfscldeleg *dp;
3998191783Srmacklem
3999214406Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
4000214406Srmacklem		if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0)
4001214406Srmacklem			dp->nfsdl_flags |= NFSCLDL_RECALL;
4002214406Srmacklem	}
4003191783Srmacklem}
4004191783Srmacklem
4005191783Srmacklem/*
4006191783Srmacklem * Relock byte ranges. Called for delegation recall and state expiry.
4007191783Srmacklem */
4008191783Srmacklemstatic int
4009191783Srmacklemnfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
4010191783Srmacklem    struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
4011191783Srmacklem    NFSPROC_T *p)
4012191783Srmacklem{
4013191783Srmacklem	struct nfscllockowner *nlp;
4014191783Srmacklem	struct nfsfh *nfhp;
4015191783Srmacklem	u_int64_t off, len;
4016191783Srmacklem	u_int32_t clidrev = 0;
4017191783Srmacklem	int error, newone, donelocally;
4018191783Srmacklem
4019191783Srmacklem	off = lop->nfslo_first;
4020191783Srmacklem	len = lop->nfslo_end - lop->nfslo_first;
4021191783Srmacklem	error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
4022228217Srmacklem	    clp, 1, NULL, lp->nfsl_lockflags, lp->nfsl_owner,
4023228217Srmacklem	    lp->nfsl_openowner, &nlp, &newone, &donelocally);
4024191783Srmacklem	if (error || donelocally)
4025191783Srmacklem		return (error);
4026191783Srmacklem	if (nmp->nm_clp != NULL)
4027191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
4028191783Srmacklem	else
4029191783Srmacklem		clidrev = 0;
4030191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
4031191783Srmacklem	error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
4032191783Srmacklem	    nfhp->nfh_len, nlp, newone, 0, off,
4033191783Srmacklem	    len, lop->nfslo_type, cred, p);
4034191783Srmacklem	if (error)
4035191783Srmacklem		nfscl_freelockowner(nlp, 0);
4036191783Srmacklem	return (error);
4037191783Srmacklem}
4038191783Srmacklem
4039191783Srmacklem/*
4040191783Srmacklem * Called to re-open a file. Basically get a vnode for the file handle
4041191783Srmacklem * and then call nfsrpc_openrpc() to do the rest.
4042191783Srmacklem */
4043191783Srmacklemstatic int
4044191783Srmacklemnfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
4045191783Srmacklem    u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
4046191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
4047191783Srmacklem{
4048191783Srmacklem	struct nfsnode *np;
4049191783Srmacklem	vnode_t vp;
4050191783Srmacklem	int error;
4051191783Srmacklem
4052191783Srmacklem	error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
4053191783Srmacklem	if (error)
4054191783Srmacklem		return (error);
4055191783Srmacklem	vp = NFSTOV(np);
4056191783Srmacklem	if (np->n_v4 != NULL) {
4057191783Srmacklem		error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
4058191783Srmacklem		    np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
4059191783Srmacklem		    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
4060191783Srmacklem		    cred, p);
4061191783Srmacklem	} else {
4062191783Srmacklem		error = EINVAL;
4063191783Srmacklem	}
4064191783Srmacklem	vrele(vp);
4065191783Srmacklem	return (error);
4066191783Srmacklem}
4067191783Srmacklem
4068191783Srmacklem/*
4069191783Srmacklem * Try an open against the server. Just call nfsrpc_openrpc(), retrying while
4070191783Srmacklem * NFSERR_DELAY. Also, try system credentials, if the passed in credentials
4071191783Srmacklem * fail.
4072191783Srmacklem */
4073191783Srmacklemstatic int
4074191783Srmacklemnfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4075191783Srmacklem    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
4076191783Srmacklem    u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
4077191783Srmacklem    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
4078191783Srmacklem{
4079191783Srmacklem	int error;
4080191783Srmacklem
4081191783Srmacklem	do {
4082191783Srmacklem		error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
4083191783Srmacklem		    mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
4084191783Srmacklem		    0, 0);
4085191783Srmacklem		if (error == NFSERR_DELAY)
4086207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfstryop");
4087191783Srmacklem	} while (error == NFSERR_DELAY);
4088191783Srmacklem	if (error == EAUTH || error == EACCES) {
4089191783Srmacklem		/* Try again using system credentials */
4090191783Srmacklem		newnfs_setroot(cred);
4091191783Srmacklem		do {
4092191783Srmacklem		    error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
4093191783Srmacklem			newfhlen, mode, op, name, namelen, ndpp, reclaim,
4094191783Srmacklem			delegtype, cred, p, 1, 0);
4095191783Srmacklem		    if (error == NFSERR_DELAY)
4096207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfstryop");
4097191783Srmacklem		} while (error == NFSERR_DELAY);
4098191783Srmacklem	}
4099191783Srmacklem	return (error);
4100191783Srmacklem}
4101191783Srmacklem
4102191783Srmacklem/*
4103191783Srmacklem * Try a byte range lock. Just loop on nfsrpc_lock() while it returns
4104191783Srmacklem * NFSERR_DELAY. Also, retry with system credentials, if the provided
4105191783Srmacklem * cred don't work.
4106191783Srmacklem */
4107191783Srmacklemstatic int
4108191783Srmacklemnfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
4109191783Srmacklem    int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
4110191783Srmacklem    u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
4111191783Srmacklem{
4112191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4113191783Srmacklem	int error;
4114191783Srmacklem
4115191783Srmacklem	do {
4116191783Srmacklem		error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
4117191783Srmacklem		    reclaim, off, len, type, cred, p, 0);
4118191783Srmacklem		if (!error && nd->nd_repstat == NFSERR_DELAY)
4119207170Srmacklem			(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4120207170Srmacklem			    "nfstrylck");
4121191783Srmacklem	} while (!error && nd->nd_repstat == NFSERR_DELAY);
4122191783Srmacklem	if (!error)
4123191783Srmacklem		error = nd->nd_repstat;
4124191783Srmacklem	if (error == EAUTH || error == EACCES) {
4125191783Srmacklem		/* Try again using root credentials */
4126191783Srmacklem		newnfs_setroot(cred);
4127191783Srmacklem		do {
4128191783Srmacklem			error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
4129191783Srmacklem			    newone, reclaim, off, len, type, cred, p, 1);
4130191783Srmacklem			if (!error && nd->nd_repstat == NFSERR_DELAY)
4131207170Srmacklem				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4132207170Srmacklem				    "nfstrylck");
4133191783Srmacklem		} while (!error && nd->nd_repstat == NFSERR_DELAY);
4134191783Srmacklem		if (!error)
4135191783Srmacklem			error = nd->nd_repstat;
4136191783Srmacklem	}
4137191783Srmacklem	return (error);
4138191783Srmacklem}
4139191783Srmacklem
4140191783Srmacklem/*
4141191783Srmacklem * Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
4142191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4143191783Srmacklem * credentials fail.
4144191783Srmacklem */
4145191783Srmacklemstatic int
4146191783Srmacklemnfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
4147191783Srmacklem    struct nfsmount *nmp, NFSPROC_T *p)
4148191783Srmacklem{
4149191783Srmacklem	int error;
4150191783Srmacklem
4151191783Srmacklem	do {
4152191783Srmacklem		error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
4153191783Srmacklem		if (error == NFSERR_DELAY)
4154207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfstrydp");
4155191783Srmacklem	} while (error == NFSERR_DELAY);
4156191783Srmacklem	if (error == EAUTH || error == EACCES) {
4157191783Srmacklem		/* Try again using system credentials */
4158191783Srmacklem		newnfs_setroot(cred);
4159191783Srmacklem		do {
4160191783Srmacklem			error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
4161191783Srmacklem			if (error == NFSERR_DELAY)
4162207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfstrydp");
4163191783Srmacklem		} while (error == NFSERR_DELAY);
4164191783Srmacklem	}
4165191783Srmacklem	return (error);
4166191783Srmacklem}
4167191783Srmacklem
4168191783Srmacklem/*
4169191783Srmacklem * Try a close against the server. Just call nfsrpc_closerpc(),
4170191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4171191783Srmacklem * credentials fail.
4172191783Srmacklem */
4173191783SrmacklemAPPLESTATIC int
4174191783Srmacklemnfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
4175191783Srmacklem    struct nfsmount *nmp, NFSPROC_T *p)
4176191783Srmacklem{
4177191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4178191783Srmacklem	int error;
4179191783Srmacklem
4180191783Srmacklem	do {
4181191783Srmacklem		error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
4182191783Srmacklem		if (error == NFSERR_DELAY)
4183207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfstrycl");
4184191783Srmacklem	} while (error == NFSERR_DELAY);
4185191783Srmacklem	if (error == EAUTH || error == EACCES) {
4186191783Srmacklem		/* Try again using system credentials */
4187191783Srmacklem		newnfs_setroot(cred);
4188191783Srmacklem		do {
4189191783Srmacklem			error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
4190191783Srmacklem			if (error == NFSERR_DELAY)
4191207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfstrycl");
4192191783Srmacklem		} while (error == NFSERR_DELAY);
4193191783Srmacklem	}
4194191783Srmacklem	return (error);
4195191783Srmacklem}
4196191783Srmacklem
4197191783Srmacklem/*
4198191783Srmacklem * Decide if a delegation on a file permits close without flushing writes
4199191783Srmacklem * to the server. This might be a big performance win in some environments.
4200191783Srmacklem * (Not useful until the client does caching on local stable storage.)
4201191783Srmacklem */
4202210786SrmacklemAPPLESTATIC int
4203191783Srmacklemnfscl_mustflush(vnode_t vp)
4204191783Srmacklem{
4205191783Srmacklem	struct nfsclclient *clp;
4206191783Srmacklem	struct nfscldeleg *dp;
4207191783Srmacklem	struct nfsnode *np;
4208191783Srmacklem	struct nfsmount *nmp;
4209191783Srmacklem
4210191783Srmacklem	np = VTONFS(vp);
4211191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4212191783Srmacklem	if (!NFSHASNFSV4(nmp))
4213210786Srmacklem		return (1);
4214191783Srmacklem	NFSLOCKCLSTATE();
4215191783Srmacklem	clp = nfscl_findcl(nmp);
4216191783Srmacklem	if (clp == NULL) {
4217191783Srmacklem		NFSUNLOCKCLSTATE();
4218210786Srmacklem		return (1);
4219191783Srmacklem	}
4220191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4221214406Srmacklem	if (dp != NULL && (dp->nfsdl_flags &
4222214406Srmacklem	    (NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) ==
4223214406Srmacklem	     NFSCLDL_WRITE &&
4224191783Srmacklem	    (dp->nfsdl_sizelimit >= np->n_size ||
4225191783Srmacklem	     !NFSHASSTRICT3530(nmp))) {
4226191783Srmacklem		NFSUNLOCKCLSTATE();
4227210786Srmacklem		return (0);
4228191783Srmacklem	}
4229191783Srmacklem	NFSUNLOCKCLSTATE();
4230210786Srmacklem	return (1);
4231191783Srmacklem}
4232191783Srmacklem
4233191783Srmacklem/*
4234191783Srmacklem * See if a (write) delegation exists for this file.
4235191783Srmacklem */
4236191783SrmacklemAPPLESTATIC int
4237191783Srmacklemnfscl_nodeleg(vnode_t vp, int writedeleg)
4238191783Srmacklem{
4239191783Srmacklem	struct nfsclclient *clp;
4240191783Srmacklem	struct nfscldeleg *dp;
4241191783Srmacklem	struct nfsnode *np;
4242191783Srmacklem	struct nfsmount *nmp;
4243191783Srmacklem
4244191783Srmacklem	np = VTONFS(vp);
4245191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4246191783Srmacklem	if (!NFSHASNFSV4(nmp))
4247191783Srmacklem		return (1);
4248191783Srmacklem	NFSLOCKCLSTATE();
4249191783Srmacklem	clp = nfscl_findcl(nmp);
4250191783Srmacklem	if (clp == NULL) {
4251191783Srmacklem		NFSUNLOCKCLSTATE();
4252191783Srmacklem		return (1);
4253191783Srmacklem	}
4254191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4255214406Srmacklem	if (dp != NULL &&
4256214406Srmacklem	    (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 &&
4257214406Srmacklem	    (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) ==
4258214406Srmacklem	     NFSCLDL_WRITE)) {
4259191783Srmacklem		NFSUNLOCKCLSTATE();
4260191783Srmacklem		return (0);
4261191783Srmacklem	}
4262191783Srmacklem	NFSUNLOCKCLSTATE();
4263191783Srmacklem	return (1);
4264191783Srmacklem}
4265191783Srmacklem
4266191783Srmacklem/*
4267191783Srmacklem * Look for an associated delegation that should be DelegReturned.
4268191783Srmacklem */
4269191783SrmacklemAPPLESTATIC int
4270191783Srmacklemnfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
4271191783Srmacklem{
4272191783Srmacklem	struct nfsclclient *clp;
4273191783Srmacklem	struct nfscldeleg *dp;
4274191783Srmacklem	struct nfsclowner *owp;
4275191783Srmacklem	struct nfscllockowner *lp;
4276191783Srmacklem	struct nfsmount *nmp;
4277191783Srmacklem	struct ucred *cred;
4278191783Srmacklem	struct nfsnode *np;
4279191783Srmacklem	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4280191783Srmacklem
4281191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4282191783Srmacklem	np = VTONFS(vp);
4283191783Srmacklem	NFSLOCKCLSTATE();
4284191783Srmacklem	/*
4285191783Srmacklem	 * Loop around waiting for:
4286191783Srmacklem	 * - outstanding I/O operations on delegations to complete
4287191783Srmacklem	 * - for a delegation on vp that has state, lock the client and
4288191783Srmacklem	 *   do a recall
4289191783Srmacklem	 * - return delegation with no state
4290191783Srmacklem	 */
4291191783Srmacklem	while (1) {
4292191783Srmacklem		clp = nfscl_findcl(nmp);
4293191783Srmacklem		if (clp == NULL) {
4294191783Srmacklem			NFSUNLOCKCLSTATE();
4295191783Srmacklem			return (retcnt);
4296191783Srmacklem		}
4297191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4298191783Srmacklem		    np->n_fhp->nfh_len);
4299191783Srmacklem		if (dp != NULL) {
4300191783Srmacklem		    /*
4301191783Srmacklem		     * Wait for outstanding I/O ops to be done.
4302191783Srmacklem		     */
4303191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4304191783Srmacklem			if (igotlock) {
4305191783Srmacklem			    nfsv4_unlock(&clp->nfsc_lock, 0);
4306191783Srmacklem			    igotlock = 0;
4307191783Srmacklem			}
4308191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4309191783Srmacklem			(void) nfsmsleep(&dp->nfsdl_rwlock,
4310191783Srmacklem			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4311191783Srmacklem			continue;
4312191783Srmacklem		    }
4313191783Srmacklem		    needsrecall = 0;
4314191783Srmacklem		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4315191783Srmacklem			if (!LIST_EMPTY(&owp->nfsow_open)) {
4316191783Srmacklem			    needsrecall = 1;
4317191783Srmacklem			    break;
4318191783Srmacklem			}
4319191783Srmacklem		    }
4320191783Srmacklem		    if (!needsrecall) {
4321191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4322191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4323191783Srmacklem				needsrecall = 1;
4324191783Srmacklem				break;
4325191783Srmacklem			    }
4326191783Srmacklem			}
4327191783Srmacklem		    }
4328191783Srmacklem		    if (needsrecall && !triedrecall) {
4329214406Srmacklem			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4330191783Srmacklem			islept = 0;
4331191783Srmacklem			while (!igotlock) {
4332191783Srmacklem			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4333222389Srmacklem				&islept, NFSCLSTATEMUTEXPTR, NULL);
4334191783Srmacklem			    if (islept)
4335191783Srmacklem				break;
4336191783Srmacklem			}
4337191783Srmacklem			if (islept)
4338191783Srmacklem			    continue;
4339191783Srmacklem			NFSUNLOCKCLSTATE();
4340191783Srmacklem			cred = newnfs_getcred();
4341191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
4342207082Srmacklem			(void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0);
4343191783Srmacklem			NFSFREECRED(cred);
4344191783Srmacklem			triedrecall = 1;
4345191783Srmacklem			NFSLOCKCLSTATE();
4346191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
4347191783Srmacklem			igotlock = 0;
4348191783Srmacklem			continue;
4349191783Srmacklem		    }
4350191783Srmacklem		    *stp = dp->nfsdl_stateid;
4351191783Srmacklem		    retcnt = 1;
4352191783Srmacklem		    nfscl_cleandeleg(dp);
4353191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4354191783Srmacklem		}
4355191783Srmacklem		if (igotlock)
4356191783Srmacklem		    nfsv4_unlock(&clp->nfsc_lock, 0);
4357191783Srmacklem		NFSUNLOCKCLSTATE();
4358191783Srmacklem		return (retcnt);
4359191783Srmacklem	}
4360191783Srmacklem}
4361191783Srmacklem
4362191783Srmacklem/*
4363191783Srmacklem * Look for associated delegation(s) that should be DelegReturned.
4364191783Srmacklem */
4365191783SrmacklemAPPLESTATIC int
4366191783Srmacklemnfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
4367191783Srmacklem    nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
4368191783Srmacklem{
4369191783Srmacklem	struct nfsclclient *clp;
4370191783Srmacklem	struct nfscldeleg *dp;
4371191783Srmacklem	struct nfsclowner *owp;
4372191783Srmacklem	struct nfscllockowner *lp;
4373191783Srmacklem	struct nfsmount *nmp;
4374191783Srmacklem	struct ucred *cred;
4375191783Srmacklem	struct nfsnode *np;
4376191783Srmacklem	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4377191783Srmacklem
4378191783Srmacklem	nmp = VFSTONFS(vnode_mount(fvp));
4379191783Srmacklem	*gotfdp = 0;
4380191783Srmacklem	*gottdp = 0;
4381191783Srmacklem	NFSLOCKCLSTATE();
4382191783Srmacklem	/*
4383191783Srmacklem	 * Loop around waiting for:
4384191783Srmacklem	 * - outstanding I/O operations on delegations to complete
4385191783Srmacklem	 * - for a delegation on fvp that has state, lock the client and
4386191783Srmacklem	 *   do a recall
4387191783Srmacklem	 * - return delegation(s) with no state.
4388191783Srmacklem	 */
4389191783Srmacklem	while (1) {
4390191783Srmacklem		clp = nfscl_findcl(nmp);
4391191783Srmacklem		if (clp == NULL) {
4392191783Srmacklem			NFSUNLOCKCLSTATE();
4393191783Srmacklem			return (retcnt);
4394191783Srmacklem		}
4395191783Srmacklem		np = VTONFS(fvp);
4396191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4397191783Srmacklem		    np->n_fhp->nfh_len);
4398191783Srmacklem		if (dp != NULL && *gotfdp == 0) {
4399191783Srmacklem		    /*
4400191783Srmacklem		     * Wait for outstanding I/O ops to be done.
4401191783Srmacklem		     */
4402191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4403191783Srmacklem			if (igotlock) {
4404191783Srmacklem			    nfsv4_unlock(&clp->nfsc_lock, 0);
4405191783Srmacklem			    igotlock = 0;
4406191783Srmacklem			}
4407191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4408191783Srmacklem			(void) nfsmsleep(&dp->nfsdl_rwlock,
4409191783Srmacklem			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4410191783Srmacklem			continue;
4411191783Srmacklem		    }
4412191783Srmacklem		    needsrecall = 0;
4413191783Srmacklem		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4414191783Srmacklem			if (!LIST_EMPTY(&owp->nfsow_open)) {
4415191783Srmacklem			    needsrecall = 1;
4416191783Srmacklem			    break;
4417191783Srmacklem			}
4418191783Srmacklem		    }
4419191783Srmacklem		    if (!needsrecall) {
4420191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4421191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4422191783Srmacklem				needsrecall = 1;
4423191783Srmacklem				break;
4424191783Srmacklem			    }
4425191783Srmacklem			}
4426191783Srmacklem		    }
4427191783Srmacklem		    if (needsrecall && !triedrecall) {
4428214406Srmacklem			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4429191783Srmacklem			islept = 0;
4430191783Srmacklem			while (!igotlock) {
4431191783Srmacklem			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4432222389Srmacklem				&islept, NFSCLSTATEMUTEXPTR, NULL);
4433191783Srmacklem			    if (islept)
4434191783Srmacklem				break;
4435191783Srmacklem			}
4436191783Srmacklem			if (islept)
4437191783Srmacklem			    continue;
4438191783Srmacklem			NFSUNLOCKCLSTATE();
4439191783Srmacklem			cred = newnfs_getcred();
4440191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
4441207082Srmacklem			(void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0);
4442191783Srmacklem			NFSFREECRED(cred);
4443191783Srmacklem			triedrecall = 1;
4444191783Srmacklem			NFSLOCKCLSTATE();
4445191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
4446191783Srmacklem			igotlock = 0;
4447191783Srmacklem			continue;
4448191783Srmacklem		    }
4449191783Srmacklem		    *fstp = dp->nfsdl_stateid;
4450191783Srmacklem		    retcnt++;
4451191783Srmacklem		    *gotfdp = 1;
4452191783Srmacklem		    nfscl_cleandeleg(dp);
4453191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4454191783Srmacklem		}
4455191783Srmacklem		if (igotlock) {
4456191783Srmacklem		    nfsv4_unlock(&clp->nfsc_lock, 0);
4457191783Srmacklem		    igotlock = 0;
4458191783Srmacklem		}
4459191783Srmacklem		if (tvp != NULL) {
4460191783Srmacklem		    np = VTONFS(tvp);
4461191783Srmacklem		    dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4462191783Srmacklem			np->n_fhp->nfh_len);
4463191783Srmacklem		    if (dp != NULL && *gottdp == 0) {
4464191783Srmacklem			/*
4465191783Srmacklem			 * Wait for outstanding I/O ops to be done.
4466191783Srmacklem			 */
4467191783Srmacklem			if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4468191783Srmacklem			    dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4469191783Srmacklem			    (void) nfsmsleep(&dp->nfsdl_rwlock,
4470191783Srmacklem				NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4471191783Srmacklem			    continue;
4472191783Srmacklem			}
4473191783Srmacklem			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4474191783Srmacklem			    if (!LIST_EMPTY(&owp->nfsow_open)) {
4475191783Srmacklem				NFSUNLOCKCLSTATE();
4476191783Srmacklem				return (retcnt);
4477191783Srmacklem			    }
4478191783Srmacklem			}
4479191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4480191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4481191783Srmacklem				NFSUNLOCKCLSTATE();
4482191783Srmacklem				return (retcnt);
4483191783Srmacklem			    }
4484191783Srmacklem			}
4485191783Srmacklem			*tstp = dp->nfsdl_stateid;
4486191783Srmacklem			retcnt++;
4487191783Srmacklem			*gottdp = 1;
4488191783Srmacklem			nfscl_cleandeleg(dp);
4489191783Srmacklem			nfscl_freedeleg(&clp->nfsc_deleg, dp);
4490191783Srmacklem		    }
4491191783Srmacklem		}
4492191783Srmacklem		NFSUNLOCKCLSTATE();
4493191783Srmacklem		return (retcnt);
4494191783Srmacklem	}
4495191783Srmacklem}
4496191783Srmacklem
4497191783Srmacklem/*
4498191783Srmacklem * Get a reference on the clientid associated with the mount point.
4499191783Srmacklem * Return 1 if success, 0 otherwise.
4500191783Srmacklem */
4501191783SrmacklemAPPLESTATIC int
4502191783Srmacklemnfscl_getref(struct nfsmount *nmp)
4503191783Srmacklem{
4504191783Srmacklem	struct nfsclclient *clp;
4505191783Srmacklem
4506191783Srmacklem	NFSLOCKCLSTATE();
4507191783Srmacklem	clp = nfscl_findcl(nmp);
4508191783Srmacklem	if (clp == NULL) {
4509191783Srmacklem		NFSUNLOCKCLSTATE();
4510191783Srmacklem		return (0);
4511191783Srmacklem	}
4512222389Srmacklem	nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL);
4513191783Srmacklem	NFSUNLOCKCLSTATE();
4514191783Srmacklem	return (1);
4515191783Srmacklem}
4516191783Srmacklem
4517191783Srmacklem/*
4518191783Srmacklem * Release a reference on a clientid acquired with the above call.
4519191783Srmacklem */
4520191783SrmacklemAPPLESTATIC void
4521191783Srmacklemnfscl_relref(struct nfsmount *nmp)
4522191783Srmacklem{
4523191783Srmacklem	struct nfsclclient *clp;
4524191783Srmacklem
4525191783Srmacklem	NFSLOCKCLSTATE();
4526191783Srmacklem	clp = nfscl_findcl(nmp);
4527191783Srmacklem	if (clp == NULL) {
4528191783Srmacklem		NFSUNLOCKCLSTATE();
4529191783Srmacklem		return;
4530191783Srmacklem	}
4531191783Srmacklem	nfsv4_relref(&clp->nfsc_lock);
4532191783Srmacklem	NFSUNLOCKCLSTATE();
4533191783Srmacklem}
4534191783Srmacklem
4535191783Srmacklem/*
4536191783Srmacklem * Save the size attribute in the delegation, since the nfsnode
4537191783Srmacklem * is going away.
4538191783Srmacklem */
4539191783SrmacklemAPPLESTATIC void
4540191783Srmacklemnfscl_reclaimnode(vnode_t vp)
4541191783Srmacklem{
4542191783Srmacklem	struct nfsclclient *clp;
4543191783Srmacklem	struct nfscldeleg *dp;
4544191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4545191783Srmacklem	struct nfsmount *nmp;
4546191783Srmacklem
4547191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4548191783Srmacklem	if (!NFSHASNFSV4(nmp))
4549191783Srmacklem		return;
4550191783Srmacklem	NFSLOCKCLSTATE();
4551191783Srmacklem	clp = nfscl_findcl(nmp);
4552191783Srmacklem	if (clp == NULL) {
4553191783Srmacklem		NFSUNLOCKCLSTATE();
4554191783Srmacklem		return;
4555191783Srmacklem	}
4556191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4557191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4558191783Srmacklem		dp->nfsdl_size = np->n_size;
4559191783Srmacklem	NFSUNLOCKCLSTATE();
4560191783Srmacklem}
4561191783Srmacklem
4562191783Srmacklem/*
4563191783Srmacklem * Get the saved size attribute in the delegation, since it is a
4564191783Srmacklem * newly allocated nfsnode.
4565191783Srmacklem */
4566191783SrmacklemAPPLESTATIC void
4567191783Srmacklemnfscl_newnode(vnode_t vp)
4568191783Srmacklem{
4569191783Srmacklem	struct nfsclclient *clp;
4570191783Srmacklem	struct nfscldeleg *dp;
4571191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4572191783Srmacklem	struct nfsmount *nmp;
4573191783Srmacklem
4574191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4575191783Srmacklem	if (!NFSHASNFSV4(nmp))
4576191783Srmacklem		return;
4577191783Srmacklem	NFSLOCKCLSTATE();
4578191783Srmacklem	clp = nfscl_findcl(nmp);
4579191783Srmacklem	if (clp == NULL) {
4580191783Srmacklem		NFSUNLOCKCLSTATE();
4581191783Srmacklem		return;
4582191783Srmacklem	}
4583191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4584191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4585191783Srmacklem		np->n_size = dp->nfsdl_size;
4586191783Srmacklem	NFSUNLOCKCLSTATE();
4587191783Srmacklem}
4588191783Srmacklem
4589191783Srmacklem/*
4590191783Srmacklem * If there is a valid write delegation for this file, set the modtime
4591191783Srmacklem * to the local clock time.
4592191783Srmacklem */
4593191783SrmacklemAPPLESTATIC void
4594191783Srmacklemnfscl_delegmodtime(vnode_t vp)
4595191783Srmacklem{
4596191783Srmacklem	struct nfsclclient *clp;
4597191783Srmacklem	struct nfscldeleg *dp;
4598191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4599191783Srmacklem	struct nfsmount *nmp;
4600191783Srmacklem
4601191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4602191783Srmacklem	if (!NFSHASNFSV4(nmp))
4603191783Srmacklem		return;
4604191783Srmacklem	NFSLOCKCLSTATE();
4605191783Srmacklem	clp = nfscl_findcl(nmp);
4606191783Srmacklem	if (clp == NULL) {
4607191783Srmacklem		NFSUNLOCKCLSTATE();
4608191783Srmacklem		return;
4609191783Srmacklem	}
4610191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4611191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
4612245909Sjhb		nanotime(&dp->nfsdl_modtime);
4613191783Srmacklem		dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
4614191783Srmacklem	}
4615191783Srmacklem	NFSUNLOCKCLSTATE();
4616191783Srmacklem}
4617191783Srmacklem
4618191783Srmacklem/*
4619191783Srmacklem * If there is a valid write delegation for this file with a modtime set,
4620191783Srmacklem * put that modtime in mtime.
4621191783Srmacklem */
4622191783SrmacklemAPPLESTATIC void
4623191783Srmacklemnfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
4624191783Srmacklem{
4625191783Srmacklem	struct nfsclclient *clp;
4626191783Srmacklem	struct nfscldeleg *dp;
4627191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4628191783Srmacklem	struct nfsmount *nmp;
4629191783Srmacklem
4630191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4631191783Srmacklem	if (!NFSHASNFSV4(nmp))
4632191783Srmacklem		return;
4633191783Srmacklem	NFSLOCKCLSTATE();
4634191783Srmacklem	clp = nfscl_findcl(nmp);
4635191783Srmacklem	if (clp == NULL) {
4636191783Srmacklem		NFSUNLOCKCLSTATE();
4637191783Srmacklem		return;
4638191783Srmacklem	}
4639191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4640191783Srmacklem	if (dp != NULL &&
4641191783Srmacklem	    (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
4642191783Srmacklem	    (NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
4643191783Srmacklem		*mtime = dp->nfsdl_modtime;
4644191783Srmacklem	NFSUNLOCKCLSTATE();
4645191783Srmacklem}
4646191783Srmacklem
4647191783Srmacklemstatic int
4648191783Srmacklemnfscl_errmap(struct nfsrv_descript *nd)
4649191783Srmacklem{
4650191783Srmacklem	short *defaulterrp, *errp;
4651191783Srmacklem
4652191783Srmacklem	if (!nd->nd_repstat)
4653191783Srmacklem		return (0);
4654191783Srmacklem	if (nd->nd_procnum == NFSPROC_NOOP)
4655191783Srmacklem		return (txdr_unsigned(nd->nd_repstat & 0xffff));
4656191783Srmacklem	if (nd->nd_repstat == EBADRPC)
4657191783Srmacklem		return (txdr_unsigned(NFSERR_BADXDR));
4658191783Srmacklem	if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
4659191783Srmacklem	    nd->nd_repstat == NFSERR_OPILLEGAL)
4660191783Srmacklem		return (txdr_unsigned(nd->nd_repstat));
4661244042Srmacklem	if (nd->nd_procnum < NFSV4OP_CBNOPS)
4662244042Srmacklem		errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
4663244042Srmacklem	else
4664244042Srmacklem		return (txdr_unsigned(nd->nd_repstat));
4665191783Srmacklem	while (*++errp)
4666191783Srmacklem		if (*errp == (short)nd->nd_repstat)
4667191783Srmacklem			return (txdr_unsigned(nd->nd_repstat));
4668191783Srmacklem	return (txdr_unsigned(*defaulterrp));
4669191783Srmacklem}
4670191783Srmacklem
4671244042Srmacklem/*
4672244042Srmacklem * Called to find/add a layout to a client.
4673244042Srmacklem * This function returns the layout with a refcnt (shared lock) upon
4674244042Srmacklem * success (returns 0) or with no lock/refcnt on the layout when an
4675244042Srmacklem * error is returned.
4676244042Srmacklem * If a layout is passed in via lypp, it is locked (exclusively locked).
4677244042Srmacklem */
4678244042SrmacklemAPPLESTATIC int
4679244042Srmacklemnfscl_layout(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4680244042Srmacklem    nfsv4stateid_t *stateidp, int retonclose,
4681244042Srmacklem    struct nfsclflayouthead *fhlp, struct nfscllayout **lypp,
4682244042Srmacklem    struct ucred *cred, NFSPROC_T *p)
4683244042Srmacklem{
4684244042Srmacklem	struct nfsclclient *clp;
4685244042Srmacklem	struct nfscllayout *lyp, *tlyp;
4686244042Srmacklem	struct nfsclflayout *flp;
4687244042Srmacklem	struct nfsnode *np = VTONFS(vp);
4688244042Srmacklem	mount_t mp;
4689244042Srmacklem	int layout_passed_in;
4690244042Srmacklem
4691244042Srmacklem	mp = nmp->nm_mountp;
4692244042Srmacklem	layout_passed_in = 1;
4693244042Srmacklem	tlyp = NULL;
4694244042Srmacklem	lyp = *lypp;
4695244042Srmacklem	if (lyp == NULL) {
4696244042Srmacklem		layout_passed_in = 0;
4697244042Srmacklem		tlyp = malloc(sizeof(*tlyp) + fhlen - 1, M_NFSLAYOUT,
4698244042Srmacklem		    M_WAITOK | M_ZERO);
4699244042Srmacklem	}
4700244042Srmacklem
4701244042Srmacklem	NFSLOCKCLSTATE();
4702244042Srmacklem	clp = nmp->nm_clp;
4703244042Srmacklem	if (clp == NULL) {
4704244042Srmacklem		if (layout_passed_in != 0)
4705244042Srmacklem			nfsv4_unlock(&lyp->nfsly_lock, 0);
4706244042Srmacklem		NFSUNLOCKCLSTATE();
4707244042Srmacklem		if (tlyp != NULL)
4708244042Srmacklem			free(tlyp, M_NFSLAYOUT);
4709244042Srmacklem		return (EPERM);
4710244042Srmacklem	}
4711244042Srmacklem	if (lyp == NULL) {
4712244042Srmacklem		/*
4713244042Srmacklem		 * Although no lyp was passed in, another thread might have
4714244042Srmacklem		 * allocated one. If one is found, just increment it's ref
4715244042Srmacklem		 * count and return it.
4716244042Srmacklem		 */
4717244042Srmacklem		lyp = nfscl_findlayout(clp, fhp, fhlen);
4718244042Srmacklem		if (lyp == NULL) {
4719244042Srmacklem			lyp = tlyp;
4720244042Srmacklem			tlyp = NULL;
4721244042Srmacklem			lyp->nfsly_stateid.seqid = stateidp->seqid;
4722244042Srmacklem			lyp->nfsly_stateid.other[0] = stateidp->other[0];
4723244042Srmacklem			lyp->nfsly_stateid.other[1] = stateidp->other[1];
4724244042Srmacklem			lyp->nfsly_stateid.other[2] = stateidp->other[2];
4725244042Srmacklem			lyp->nfsly_lastbyte = 0;
4726244042Srmacklem			LIST_INIT(&lyp->nfsly_flayread);
4727244042Srmacklem			LIST_INIT(&lyp->nfsly_flayrw);
4728244042Srmacklem			LIST_INIT(&lyp->nfsly_recall);
4729244042Srmacklem			lyp->nfsly_filesid[0] = np->n_vattr.na_filesid[0];
4730244042Srmacklem			lyp->nfsly_filesid[1] = np->n_vattr.na_filesid[1];
4731244042Srmacklem			lyp->nfsly_clp = clp;
4732244042Srmacklem			lyp->nfsly_flags = (retonclose != 0) ?
4733244042Srmacklem			    (NFSLY_FILES | NFSLY_RETONCLOSE) : NFSLY_FILES;
4734244042Srmacklem			lyp->nfsly_fhlen = fhlen;
4735244042Srmacklem			NFSBCOPY(fhp, lyp->nfsly_fh, fhlen);
4736244042Srmacklem			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4737244042Srmacklem			LIST_INSERT_HEAD(NFSCLLAYOUTHASH(clp, fhp, fhlen), lyp,
4738244042Srmacklem			    nfsly_hash);
4739244042Srmacklem			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4740244042Srmacklem			nfscl_layoutcnt++;
4741244042Srmacklem		} else {
4742244042Srmacklem			if (retonclose != 0)
4743244042Srmacklem				lyp->nfsly_flags |= NFSLY_RETONCLOSE;
4744244042Srmacklem			TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
4745244042Srmacklem			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4746244042Srmacklem			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4747244042Srmacklem		}
4748244042Srmacklem		nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
4749244042Srmacklem		if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
4750244042Srmacklem			NFSUNLOCKCLSTATE();
4751244042Srmacklem			if (tlyp != NULL)
4752244042Srmacklem				free(tlyp, M_NFSLAYOUT);
4753244042Srmacklem			return (EPERM);
4754244042Srmacklem		}
4755244042Srmacklem		*lypp = lyp;
4756244042Srmacklem	} else
4757244042Srmacklem		lyp->nfsly_stateid.seqid = stateidp->seqid;
4758244042Srmacklem
4759244042Srmacklem	/* Merge the new list of File Layouts into the list. */
4760244042Srmacklem	flp = LIST_FIRST(fhlp);
4761244042Srmacklem	if (flp != NULL) {
4762244042Srmacklem		if (flp->nfsfl_iomode == NFSLAYOUTIOMODE_READ)
4763244042Srmacklem			nfscl_mergeflayouts(&lyp->nfsly_flayread, fhlp);
4764244042Srmacklem		else
4765244042Srmacklem			nfscl_mergeflayouts(&lyp->nfsly_flayrw, fhlp);
4766244042Srmacklem	}
4767244042Srmacklem	if (layout_passed_in != 0)
4768244042Srmacklem		nfsv4_unlock(&lyp->nfsly_lock, 1);
4769244042Srmacklem	NFSUNLOCKCLSTATE();
4770244042Srmacklem	if (tlyp != NULL)
4771244042Srmacklem		free(tlyp, M_NFSLAYOUT);
4772244042Srmacklem	return (0);
4773244042Srmacklem}
4774244042Srmacklem
4775244042Srmacklem/*
4776244042Srmacklem * Search for a layout by MDS file handle.
4777244042Srmacklem * If one is found, it is returned with a refcnt (shared lock) iff
4778244042Srmacklem * retflpp returned non-NULL and locked (exclusive locked) iff retflpp is
4779244042Srmacklem * returned NULL.
4780244042Srmacklem */
4781244042Srmacklemstruct nfscllayout *
4782244042Srmacklemnfscl_getlayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen,
4783244042Srmacklem    uint64_t off, struct nfsclflayout **retflpp, int *recalledp)
4784244042Srmacklem{
4785244042Srmacklem	struct nfscllayout *lyp;
4786244042Srmacklem	mount_t mp;
4787244042Srmacklem	int error, igotlock;
4788244042Srmacklem
4789244042Srmacklem	mp = clp->nfsc_nmp->nm_mountp;
4790244042Srmacklem	*recalledp = 0;
4791244042Srmacklem	*retflpp = NULL;
4792244042Srmacklem	NFSLOCKCLSTATE();
4793244042Srmacklem	lyp = nfscl_findlayout(clp, fhp, fhlen);
4794244042Srmacklem	if (lyp != NULL) {
4795244042Srmacklem		if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) {
4796244042Srmacklem			TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
4797244042Srmacklem			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4798244042Srmacklem			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4799244042Srmacklem			error = nfscl_findlayoutforio(lyp, off,
4800244042Srmacklem			    NFSV4OPEN_ACCESSREAD, retflpp);
4801244042Srmacklem			if (error == 0)
4802244042Srmacklem				nfsv4_getref(&lyp->nfsly_lock, NULL,
4803244042Srmacklem				    NFSCLSTATEMUTEXPTR, mp);
4804244042Srmacklem			else {
4805244042Srmacklem				do {
4806244042Srmacklem					igotlock = nfsv4_lock(&lyp->nfsly_lock,
4807244042Srmacklem					    1, NULL, NFSCLSTATEMUTEXPTR, mp);
4808244042Srmacklem				} while (igotlock == 0 &&
4809244042Srmacklem				    (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0);
4810244042Srmacklem				*retflpp = NULL;
4811244042Srmacklem			}
4812244042Srmacklem			if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
4813244042Srmacklem				lyp = NULL;
4814244042Srmacklem				*recalledp = 1;
4815244042Srmacklem			}
4816244042Srmacklem		} else {
4817244042Srmacklem			lyp = NULL;
4818244042Srmacklem			*recalledp = 1;
4819244042Srmacklem		}
4820244042Srmacklem	}
4821244042Srmacklem	NFSUNLOCKCLSTATE();
4822244042Srmacklem	return (lyp);
4823244042Srmacklem}
4824244042Srmacklem
4825244042Srmacklem/*
4826244042Srmacklem * Search for a layout by MDS file handle. If one is found that is marked
4827244042Srmacklem * "return on close", delete it, since it should now be forgotten.
4828244042Srmacklem */
4829244042Srmacklemstatic void
4830244042Srmacklemnfscl_retoncloselayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen)
4831244042Srmacklem{
4832244042Srmacklem	struct nfscllayout *lyp;
4833244042Srmacklem
4834244042Srmacklemtryagain:
4835244042Srmacklem	lyp = nfscl_findlayout(clp, fhp, fhlen);
4836244042Srmacklem	if (lyp != NULL && (lyp->nfsly_flags & NFSLY_RETONCLOSE) != 0) {
4837244042Srmacklem		/*
4838244042Srmacklem		 * Wait for outstanding I/O ops to be done.
4839244042Srmacklem		 */
4840244042Srmacklem		if (lyp->nfsly_lock.nfslock_usecnt != 0 ||
4841244042Srmacklem		    lyp->nfsly_lock.nfslock_lock != 0) {
4842244042Srmacklem			lyp->nfsly_lock.nfslock_lock |= NFSV4LOCK_WANTED;
4843244042Srmacklem			(void)mtx_sleep(&lyp->nfsly_lock,
4844244042Srmacklem			    NFSCLSTATEMUTEXPTR, PZERO, "nfslyc", 0);
4845244042Srmacklem			goto tryagain;
4846244042Srmacklem		}
4847244042Srmacklem		nfscl_freelayout(lyp);
4848244042Srmacklem	}
4849244042Srmacklem}
4850244042Srmacklem
4851244042Srmacklem/*
4852244042Srmacklem * Dereference a layout.
4853244042Srmacklem */
4854244042Srmacklemvoid
4855244042Srmacklemnfscl_rellayout(struct nfscllayout *lyp, int exclocked)
4856244042Srmacklem{
4857244042Srmacklem
4858244042Srmacklem	NFSLOCKCLSTATE();
4859244042Srmacklem	if (exclocked != 0)
4860244042Srmacklem		nfsv4_unlock(&lyp->nfsly_lock, 0);
4861244042Srmacklem	else
4862244042Srmacklem		nfsv4_relref(&lyp->nfsly_lock);
4863244042Srmacklem	NFSUNLOCKCLSTATE();
4864244042Srmacklem}
4865244042Srmacklem
4866244042Srmacklem/*
4867244042Srmacklem * Search for a devinfo by deviceid. If one is found, return it after
4868244042Srmacklem * acquiring a reference count on it.
4869244042Srmacklem */
4870244042Srmacklemstruct nfscldevinfo *
4871244042Srmacklemnfscl_getdevinfo(struct nfsclclient *clp, uint8_t *deviceid,
4872244042Srmacklem    struct nfscldevinfo *dip)
4873244042Srmacklem{
4874244042Srmacklem
4875244042Srmacklem	NFSLOCKCLSTATE();
4876244042Srmacklem	if (dip == NULL)
4877244042Srmacklem		dip = nfscl_finddevinfo(clp, deviceid);
4878244042Srmacklem	if (dip != NULL)
4879244042Srmacklem		dip->nfsdi_refcnt++;
4880244042Srmacklem	NFSUNLOCKCLSTATE();
4881244042Srmacklem	return (dip);
4882244042Srmacklem}
4883244042Srmacklem
4884244042Srmacklem/*
4885244042Srmacklem * Dereference a devinfo structure.
4886244042Srmacklem */
4887244042Srmacklemstatic void
4888244042Srmacklemnfscl_reldevinfo_locked(struct nfscldevinfo *dip)
4889244042Srmacklem{
4890244042Srmacklem
4891244042Srmacklem	dip->nfsdi_refcnt--;
4892244042Srmacklem	if (dip->nfsdi_refcnt == 0)
4893244042Srmacklem		wakeup(&dip->nfsdi_refcnt);
4894244042Srmacklem}
4895244042Srmacklem
4896244042Srmacklem/*
4897244042Srmacklem * Dereference a devinfo structure.
4898244042Srmacklem */
4899244042Srmacklemvoid
4900244042Srmacklemnfscl_reldevinfo(struct nfscldevinfo *dip)
4901244042Srmacklem{
4902244042Srmacklem
4903244042Srmacklem	NFSLOCKCLSTATE();
4904244042Srmacklem	nfscl_reldevinfo_locked(dip);
4905244042Srmacklem	NFSUNLOCKCLSTATE();
4906244042Srmacklem}
4907244042Srmacklem
4908244042Srmacklem/*
4909244042Srmacklem * Find a layout for this file handle. Return NULL upon failure.
4910244042Srmacklem */
4911244042Srmacklemstatic struct nfscllayout *
4912244042Srmacklemnfscl_findlayout(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
4913244042Srmacklem{
4914244042Srmacklem	struct nfscllayout *lyp;
4915244042Srmacklem
4916244042Srmacklem	LIST_FOREACH(lyp, NFSCLLAYOUTHASH(clp, fhp, fhlen), nfsly_hash)
4917244042Srmacklem		if (lyp->nfsly_fhlen == fhlen &&
4918244042Srmacklem		    !NFSBCMP(lyp->nfsly_fh, fhp, fhlen))
4919244042Srmacklem			break;
4920244042Srmacklem	return (lyp);
4921244042Srmacklem}
4922244042Srmacklem
4923244042Srmacklem/*
4924244042Srmacklem * Find a devinfo for this deviceid. Return NULL upon failure.
4925244042Srmacklem */
4926244042Srmacklemstatic struct nfscldevinfo *
4927244042Srmacklemnfscl_finddevinfo(struct nfsclclient *clp, uint8_t *deviceid)
4928244042Srmacklem{
4929244042Srmacklem	struct nfscldevinfo *dip;
4930244042Srmacklem
4931244042Srmacklem	LIST_FOREACH(dip, &clp->nfsc_devinfo, nfsdi_list)
4932244042Srmacklem		if (NFSBCMP(dip->nfsdi_deviceid, deviceid, NFSX_V4DEVICEID)
4933244042Srmacklem		    == 0)
4934244042Srmacklem			break;
4935244042Srmacklem	return (dip);
4936244042Srmacklem}
4937244042Srmacklem
4938244042Srmacklem/*
4939244042Srmacklem * Merge the new file layout list into the main one, maintaining it in
4940244042Srmacklem * increasing offset order.
4941244042Srmacklem */
4942244042Srmacklemstatic void
4943244042Srmacklemnfscl_mergeflayouts(struct nfsclflayouthead *fhlp,
4944244042Srmacklem    struct nfsclflayouthead *newfhlp)
4945244042Srmacklem{
4946244042Srmacklem	struct nfsclflayout *flp, *nflp, *prevflp, *tflp;
4947244042Srmacklem
4948244042Srmacklem	flp = LIST_FIRST(fhlp);
4949244042Srmacklem	prevflp = NULL;
4950244042Srmacklem	LIST_FOREACH_SAFE(nflp, newfhlp, nfsfl_list, tflp) {
4951244042Srmacklem		while (flp != NULL && flp->nfsfl_off < nflp->nfsfl_off) {
4952244042Srmacklem			prevflp = flp;
4953244042Srmacklem			flp = LIST_NEXT(flp, nfsfl_list);
4954244042Srmacklem		}
4955244042Srmacklem		if (prevflp == NULL)
4956244042Srmacklem			LIST_INSERT_HEAD(fhlp, nflp, nfsfl_list);
4957244042Srmacklem		else
4958244042Srmacklem			LIST_INSERT_AFTER(prevflp, nflp, nfsfl_list);
4959244042Srmacklem		prevflp = nflp;
4960244042Srmacklem	}
4961244042Srmacklem}
4962244042Srmacklem
4963244042Srmacklem/*
4964244042Srmacklem * Add this nfscldevinfo to the client, if it doesn't already exist.
4965244042Srmacklem * This function consumes the structure pointed at by dip, if not NULL.
4966244042Srmacklem */
4967244042SrmacklemAPPLESTATIC int
4968244042Srmacklemnfscl_adddevinfo(struct nfsmount *nmp, struct nfscldevinfo *dip,
4969244042Srmacklem    struct nfsclflayout *flp)
4970244042Srmacklem{
4971244042Srmacklem	struct nfsclclient *clp;
4972244042Srmacklem	struct nfscldevinfo *tdip;
4973244042Srmacklem
4974244042Srmacklem	NFSLOCKCLSTATE();
4975244042Srmacklem	clp = nmp->nm_clp;
4976244042Srmacklem	if (clp == NULL) {
4977244042Srmacklem		NFSUNLOCKCLSTATE();
4978244042Srmacklem		if (dip != NULL)
4979244042Srmacklem			free(dip, M_NFSDEVINFO);
4980244042Srmacklem		return (ENODEV);
4981244042Srmacklem	}
4982244042Srmacklem	tdip = nfscl_finddevinfo(clp, flp->nfsfl_dev);
4983244042Srmacklem	if (tdip != NULL) {
4984244042Srmacklem		tdip->nfsdi_layoutrefs++;
4985244042Srmacklem		flp->nfsfl_devp = tdip;
4986244042Srmacklem		nfscl_reldevinfo_locked(tdip);
4987244042Srmacklem		NFSUNLOCKCLSTATE();
4988244042Srmacklem		if (dip != NULL)
4989244042Srmacklem			free(dip, M_NFSDEVINFO);
4990244042Srmacklem		return (0);
4991244042Srmacklem	}
4992244042Srmacklem	if (dip != NULL) {
4993244042Srmacklem		LIST_INSERT_HEAD(&clp->nfsc_devinfo, dip, nfsdi_list);
4994244042Srmacklem		dip->nfsdi_layoutrefs = 1;
4995244042Srmacklem		flp->nfsfl_devp = dip;
4996244042Srmacklem	}
4997244042Srmacklem	NFSUNLOCKCLSTATE();
4998244042Srmacklem	if (dip == NULL)
4999244042Srmacklem		return (ENODEV);
5000244042Srmacklem	return (0);
5001244042Srmacklem}
5002244042Srmacklem
5003244042Srmacklem/*
5004244042Srmacklem * Free up a layout structure and associated file layout structure(s).
5005244042Srmacklem */
5006244042SrmacklemAPPLESTATIC void
5007244042Srmacklemnfscl_freelayout(struct nfscllayout *layp)
5008244042Srmacklem{
5009244042Srmacklem	struct nfsclflayout *flp, *nflp;
5010244042Srmacklem	struct nfsclrecalllayout *rp, *nrp;
5011244042Srmacklem
5012244042Srmacklem	LIST_FOREACH_SAFE(flp, &layp->nfsly_flayread, nfsfl_list, nflp) {
5013244042Srmacklem		LIST_REMOVE(flp, nfsfl_list);
5014244042Srmacklem		nfscl_freeflayout(flp);
5015244042Srmacklem	}
5016244042Srmacklem	LIST_FOREACH_SAFE(flp, &layp->nfsly_flayrw, nfsfl_list, nflp) {
5017244042Srmacklem		LIST_REMOVE(flp, nfsfl_list);
5018244042Srmacklem		nfscl_freeflayout(flp);
5019244042Srmacklem	}
5020244042Srmacklem	LIST_FOREACH_SAFE(rp, &layp->nfsly_recall, nfsrecly_list, nrp) {
5021244042Srmacklem		LIST_REMOVE(rp, nfsrecly_list);
5022244042Srmacklem		free(rp, M_NFSLAYRECALL);
5023244042Srmacklem	}
5024244042Srmacklem	nfscl_layoutcnt--;
5025244042Srmacklem	free(layp, M_NFSLAYOUT);
5026244042Srmacklem}
5027244042Srmacklem
5028244042Srmacklem/*
5029244042Srmacklem * Free up a file layout structure.
5030244042Srmacklem */
5031244042SrmacklemAPPLESTATIC void
5032244042Srmacklemnfscl_freeflayout(struct nfsclflayout *flp)
5033244042Srmacklem{
5034244042Srmacklem	int i;
5035244042Srmacklem
5036244042Srmacklem	for (i = 0; i < flp->nfsfl_fhcnt; i++)
5037244042Srmacklem		free(flp->nfsfl_fh[i], M_NFSFH);
5038244042Srmacklem	if (flp->nfsfl_devp != NULL)
5039244042Srmacklem		flp->nfsfl_devp->nfsdi_layoutrefs--;
5040244042Srmacklem	free(flp, M_NFSFLAYOUT);
5041244042Srmacklem}
5042244042Srmacklem
5043244042Srmacklem/*
5044244042Srmacklem * Free up a file layout devinfo structure.
5045244042Srmacklem */
5046244042SrmacklemAPPLESTATIC void
5047244042Srmacklemnfscl_freedevinfo(struct nfscldevinfo *dip)
5048244042Srmacklem{
5049244042Srmacklem
5050244042Srmacklem	free(dip, M_NFSDEVINFO);
5051244042Srmacklem}
5052244042Srmacklem
5053244042Srmacklem/*
5054244042Srmacklem * Mark any layouts that match as recalled.
5055244042Srmacklem */
5056244042Srmacklemstatic int
5057244042Srmacklemnfscl_layoutrecall(int recalltype, struct nfscllayout *lyp, uint32_t iomode,
5058244042Srmacklem    uint64_t off, uint64_t len, uint32_t stateseqid,
5059244042Srmacklem    struct nfsclrecalllayout *recallp)
5060244042Srmacklem{
5061244042Srmacklem	struct nfsclrecalllayout *rp, *orp;
5062244042Srmacklem
5063244042Srmacklem	recallp->nfsrecly_recalltype = recalltype;
5064244042Srmacklem	recallp->nfsrecly_iomode = iomode;
5065244042Srmacklem	recallp->nfsrecly_stateseqid = stateseqid;
5066244042Srmacklem	recallp->nfsrecly_off = off;
5067244042Srmacklem	recallp->nfsrecly_len = len;
5068244042Srmacklem	/*
5069244042Srmacklem	 * Order the list as file returns first, followed by fsid and any
5070244042Srmacklem	 * returns, both in increasing stateseqid order.
5071244042Srmacklem	 * Note that the seqids wrap around, so 1 is after 0xffffffff.
5072244042Srmacklem	 * (I'm not sure this is correct because I find RFC5661 confusing
5073244042Srmacklem	 *  on this, but hopefully it will work ok.)
5074244042Srmacklem	 */
5075244042Srmacklem	orp = NULL;
5076244042Srmacklem	LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5077244042Srmacklem		orp = rp;
5078244042Srmacklem		if ((recalltype == NFSLAYOUTRETURN_FILE &&
5079244042Srmacklem		     (rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE ||
5080244042Srmacklem		      nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) ||
5081244042Srmacklem		    (recalltype != NFSLAYOUTRETURN_FILE &&
5082244042Srmacklem		     rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE &&
5083244042Srmacklem		     nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) {
5084244042Srmacklem			LIST_INSERT_BEFORE(rp, recallp, nfsrecly_list);
5085244042Srmacklem			break;
5086244042Srmacklem		}
5087244042Srmacklem	}
5088244042Srmacklem	if (rp == NULL) {
5089244042Srmacklem		if (orp == NULL)
5090244042Srmacklem			LIST_INSERT_HEAD(&lyp->nfsly_recall, recallp,
5091244042Srmacklem			    nfsrecly_list);
5092244042Srmacklem		else
5093244042Srmacklem			LIST_INSERT_AFTER(orp, recallp, nfsrecly_list);
5094244042Srmacklem	}
5095244042Srmacklem	lyp->nfsly_flags |= NFSLY_RECALL;
5096244042Srmacklem	return (0);
5097244042Srmacklem}
5098244042Srmacklem
5099244042Srmacklem/*
5100244042Srmacklem * Compare the two seqids for ordering. The trick is that the seqids can
5101244042Srmacklem * wrap around from 0xffffffff->0, so check for the cases where one
5102244042Srmacklem * has wrapped around.
5103244042Srmacklem * Return 1 if seqid1 comes before seqid2, 0 otherwise.
5104244042Srmacklem */
5105244042Srmacklemstatic int
5106244042Srmacklemnfscl_seq(uint32_t seqid1, uint32_t seqid2)
5107244042Srmacklem{
5108244042Srmacklem
5109244042Srmacklem	if (seqid2 > seqid1 && (seqid2 - seqid1) >= 0x7fffffff)
5110244042Srmacklem		/* seqid2 has wrapped around. */
5111244042Srmacklem		return (0);
5112244042Srmacklem	if (seqid1 > seqid2 && (seqid1 - seqid2) >= 0x7fffffff)
5113244042Srmacklem		/* seqid1 has wrapped around. */
5114244042Srmacklem		return (1);
5115244042Srmacklem	if (seqid1 <= seqid2)
5116244042Srmacklem		return (1);
5117244042Srmacklem	return (0);
5118244042Srmacklem}
5119244042Srmacklem
5120244042Srmacklem/*
5121244042Srmacklem * Do a layout return for each of the recalls.
5122244042Srmacklem */
5123244042Srmacklemstatic void
5124244042Srmacklemnfscl_layoutreturn(struct nfsmount *nmp, struct nfscllayout *lyp,
5125244042Srmacklem    struct ucred *cred, NFSPROC_T *p)
5126244042Srmacklem{
5127244042Srmacklem	struct nfsclrecalllayout *rp;
5128244042Srmacklem	nfsv4stateid_t stateid;
5129244042Srmacklem
5130244042Srmacklem	NFSBCOPY(lyp->nfsly_stateid.other, stateid.other, NFSX_STATEIDOTHER);
5131244042Srmacklem	LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5132244042Srmacklem		stateid.seqid = rp->nfsrecly_stateseqid;
5133244042Srmacklem		(void)nfsrpc_layoutreturn(nmp, lyp->nfsly_fh,
5134244042Srmacklem		    lyp->nfsly_fhlen, 0, NFSLAYOUT_NFSV4_1_FILES,
5135244042Srmacklem		    rp->nfsrecly_iomode, rp->nfsrecly_recalltype,
5136244042Srmacklem		    rp->nfsrecly_off, rp->nfsrecly_len,
5137244042Srmacklem		    &stateid, 0, NULL, cred, p, NULL);
5138244042Srmacklem	}
5139244042Srmacklem}
5140244042Srmacklem
5141244042Srmacklem/*
5142244042Srmacklem * Do the layout commit for a file layout.
5143244042Srmacklem */
5144244042Srmacklemstatic void
5145244042Srmacklemnfscl_dolayoutcommit(struct nfsmount *nmp, struct nfscllayout *lyp,
5146244042Srmacklem    struct ucred *cred, NFSPROC_T *p)
5147244042Srmacklem{
5148252074Srmacklem	struct nfsclflayout *flp;
5149252074Srmacklem	uint64_t len;
5150244042Srmacklem	int error;
5151244042Srmacklem
5152252074Srmacklem	LIST_FOREACH(flp, &lyp->nfsly_flayrw, nfsfl_list) {
5153252074Srmacklem		if (flp->nfsfl_off <= lyp->nfsly_lastbyte) {
5154252074Srmacklem			len = flp->nfsfl_end - flp->nfsfl_off;
5155252074Srmacklem			error = nfsrpc_layoutcommit(nmp, lyp->nfsly_fh,
5156252074Srmacklem			    lyp->nfsly_fhlen, 0, flp->nfsfl_off, len,
5157252074Srmacklem			    lyp->nfsly_lastbyte, &lyp->nfsly_stateid,
5158252074Srmacklem			    NFSLAYOUT_NFSV4_1_FILES, 0, NULL, cred, p, NULL);
5159252100Srmacklem			NFSCL_DEBUG(4, "layoutcommit err=%d\n", error);
5160252074Srmacklem			if (error == NFSERR_NOTSUPP) {
5161252074Srmacklem				/* If not supported, don't bother doing it. */
5162252074Srmacklem				NFSLOCKMNT(nmp);
5163252074Srmacklem				nmp->nm_state |= NFSSTA_NOLAYOUTCOMMIT;
5164252074Srmacklem				NFSUNLOCKMNT(nmp);
5165252074Srmacklem				break;
5166252074Srmacklem			}
5167252074Srmacklem		}
5168244042Srmacklem	}
5169244042Srmacklem}
5170244042Srmacklem
5171244042Srmacklem/*
5172244042Srmacklem * Commit all layouts for a file (vnode).
5173244042Srmacklem */
5174244042Srmacklemint
5175244042Srmacklemnfscl_layoutcommit(vnode_t vp, NFSPROC_T *p)
5176244042Srmacklem{
5177244042Srmacklem	struct nfsclclient *clp;
5178244042Srmacklem	struct nfscllayout *lyp;
5179244042Srmacklem	struct nfsnode *np = VTONFS(vp);
5180244042Srmacklem	mount_t mp;
5181244042Srmacklem	struct nfsmount *nmp;
5182244042Srmacklem
5183244042Srmacklem	mp = vnode_mount(vp);
5184244042Srmacklem	nmp = VFSTONFS(mp);
5185244042Srmacklem	if (NFSHASNOLAYOUTCOMMIT(nmp))
5186244042Srmacklem		return (0);
5187244042Srmacklem	NFSLOCKCLSTATE();
5188244042Srmacklem	clp = nmp->nm_clp;
5189244042Srmacklem	if (clp == NULL) {
5190244042Srmacklem		NFSUNLOCKCLSTATE();
5191244042Srmacklem		return (EPERM);
5192244042Srmacklem	}
5193244042Srmacklem	lyp = nfscl_findlayout(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5194244042Srmacklem	if (lyp == NULL) {
5195244042Srmacklem		NFSUNLOCKCLSTATE();
5196244042Srmacklem		return (EPERM);
5197244042Srmacklem	}
5198244042Srmacklem	nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
5199244042Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
5200244042Srmacklem		NFSUNLOCKCLSTATE();
5201244042Srmacklem		return (EPERM);
5202244042Srmacklem	}
5203244042Srmacklemtryagain:
5204244042Srmacklem	if ((lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
5205244042Srmacklem		lyp->nfsly_flags &= ~NFSLY_WRITTEN;
5206244042Srmacklem		NFSUNLOCKCLSTATE();
5207244042Srmacklem		NFSCL_DEBUG(4, "do layoutcommit2\n");
5208244042Srmacklem		nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, NFSPROCCRED(p), p);
5209244042Srmacklem		NFSLOCKCLSTATE();
5210244042Srmacklem		goto tryagain;
5211244042Srmacklem	}
5212244042Srmacklem	nfsv4_relref(&lyp->nfsly_lock);
5213244042Srmacklem	NFSUNLOCKCLSTATE();
5214244042Srmacklem	return (0);
5215244042Srmacklem}
5216244042Srmacklem
5217