nfs_clstate.c revision 317404
1/*-
2 * Copyright (c) 2009 Rick Macklem, University of Guelph
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: stable/10/sys/fs/nfsclient/nfs_clstate.c 317404 2017-04-25 11:36:25Z rmacklem $");
30
31/*
32 * These functions implement the client side state handling for NFSv4.
33 * NFSv4 state handling:
34 * - A lockowner is used to determine lock contention, so it
35 *   corresponds directly to a Posix pid. (1 to 1 mapping)
36 * - The correct granularity of an OpenOwner is not nearly so
37 *   obvious. An OpenOwner does the following:
38 *   - provides a serial sequencing of Open/Close/Lock-with-new-lockowner
39 *   - is used to check for Open/Share contention (not applicable to
40 *     this client, since all Opens are Deny_None)
41 *   As such, I considered both extreme.
42 *   1 OpenOwner per ClientID - Simple to manage, but fully serializes
43 *   all Open, Close and Lock (with a new lockowner) Ops.
44 *   1 OpenOwner for each Open - This one results in an OpenConfirm for
45 *   every Open, for most servers.
46 *   So, I chose to use the same mapping as I did for LockOwnwers.
47 *   The main concern here is that you can end up with multiple Opens
48 *   for the same File Handle, but on different OpenOwners (opens
49 *   inherited from parents, grandparents...) and you do not know
50 *   which of these the vnodeop close applies to. This is handled by
51 *   delaying the Close Op(s) until all of the Opens have been closed.
52 *   (It is not yet obvious if this is the correct granularity.)
53 * - How the code handles serialization:
54 *   - For the ClientId, it uses an exclusive lock while getting its
55 *     SetClientId and during recovery. Otherwise, it uses a shared
56 *     lock via a reference count.
57 *   - For the rest of the data structures, it uses an SMP mutex
58 *     (once the nfs client is SMP safe) and doesn't sleep while
59 *     manipulating the linked lists.
60 *   - The serialization of Open/Close/Lock/LockU falls out in the
61 *     "wash", since OpenOwners and LockOwners are both mapped from
62 *     Posix pid. In other words, there is only one Posix pid using
63 *     any given owner, so that owner is serialized. (If you change
64 *     the granularity of the OpenOwner, then code must be added to
65 *     serialize Ops on the OpenOwner.)
66 * - When to get rid of OpenOwners and LockOwners.
67 *   - The function nfscl_cleanup_common() is executed after a process exits.
68 *     It goes through the client list looking for all Open and Lock Owners.
69 *     When one is found, it is marked "defunct" or in the case of
70 *     an OpenOwner without any Opens, freed.
71 *     The renew thread scans for defunct Owners and gets rid of them,
72 *     if it can. The LockOwners will also be deleted when the
73 *     associated Open is closed.
74 *   - If the LockU or Close Op(s) fail during close in a way
75 *     that could be recovered upon retry, they are relinked to the
76 *     ClientId's defunct open list and retried by the renew thread
77 *     until they succeed or an unmount/recovery occurs.
78 *     (Since we are done with them, they do not need to be recovered.)
79 */
80
81#ifndef APPLEKEXT
82#include <fs/nfs/nfsport.h>
83
84/*
85 * Global variables
86 */
87extern struct nfsstats newnfsstats;
88extern struct nfsreqhead nfsd_reqq;
89extern u_int32_t newnfs_false, newnfs_true;
90extern int nfscl_debuglevel;
91NFSREQSPINLOCK;
92NFSCLSTATEMUTEX;
93int nfscl_inited = 0;
94struct nfsclhead nfsclhead;	/* Head of clientid list */
95int nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
96int nfscl_layouthighwater = NFSCLLAYOUTHIGHWATER;
97#endif	/* !APPLEKEXT */
98
99static int nfscl_delegcnt = 0;
100static int nfscl_layoutcnt = 0;
101static int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *,
102    u_int8_t *, u_int32_t, struct nfscllockowner **, struct nfsclopen **);
103static void nfscl_clrelease(struct nfsclclient *);
104static void nfscl_cleanclient(struct nfsclclient *);
105static void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
106    struct ucred *, NFSPROC_T *);
107static int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
108    struct nfsmount *, struct ucred *, NFSPROC_T *);
109static void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *);
110static void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
111    struct nfscllock *, int);
112static int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
113    struct nfscllock **, int);
114static void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *);
115static u_int32_t nfscl_nextcbident(void);
116static mount_t nfscl_getmnt(int, uint8_t *, u_int32_t, struct nfsclclient **);
117static struct nfsclclient *nfscl_getclnt(u_int32_t);
118static struct nfsclclient *nfscl_getclntsess(uint8_t *);
119static struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *,
120    int);
121static void nfscl_retoncloselayout(struct nfsclclient *, uint8_t *, int);
122static void nfscl_reldevinfo_locked(struct nfscldevinfo *);
123static struct nfscllayout *nfscl_findlayout(struct nfsclclient *, u_int8_t *,
124    int);
125static struct nfscldevinfo *nfscl_finddevinfo(struct nfsclclient *, uint8_t *);
126static int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *,
127    u_int8_t *, struct nfscllock **);
128static void nfscl_freealllocks(struct nfscllockownerhead *, int);
129static int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int,
130    struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **);
131static void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *,
132    struct nfsclowner **, struct nfsclowner **, struct nfsclopen **,
133    struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *);
134static int nfscl_moveopen(vnode_t , struct nfsclclient *,
135    struct nfsmount *, struct nfsclopen *, struct nfsclowner *,
136    struct nfscldeleg *, struct ucred *, NFSPROC_T *);
137static void nfscl_totalrecall(struct nfsclclient *);
138static int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *,
139    struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *);
140static int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int,
141    u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int,
142    struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *);
143static int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
144    int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short,
145    struct ucred *, NFSPROC_T *);
146static int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
147    struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
148static void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *);
149static int nfscl_errmap(struct nfsrv_descript *, u_int32_t);
150static void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
151static int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
152    struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int);
153static void nfscl_freeopenowner(struct nfsclowner *, int);
154static void nfscl_cleandeleg(struct nfscldeleg *);
155static int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
156    struct nfsmount *, NFSPROC_T *);
157static void nfscl_emptylockowner(struct nfscllockowner *,
158    struct nfscllockownerfhhead *);
159static void nfscl_mergeflayouts(struct nfsclflayouthead *,
160    struct nfsclflayouthead *);
161static int nfscl_layoutrecall(int, struct nfscllayout *, uint32_t, uint64_t,
162    uint64_t, uint32_t, struct nfsclrecalllayout *);
163static int nfscl_seq(uint32_t, uint32_t);
164static void nfscl_layoutreturn(struct nfsmount *, struct nfscllayout *,
165    struct ucred *, NFSPROC_T *);
166static void nfscl_dolayoutcommit(struct nfsmount *, struct nfscllayout *,
167    struct ucred *, NFSPROC_T *);
168
169static short nfscberr_null[] = {
170	0,
171	0,
172};
173
174static short nfscberr_getattr[] = {
175	NFSERR_RESOURCE,
176	NFSERR_BADHANDLE,
177	NFSERR_BADXDR,
178	NFSERR_RESOURCE,
179	NFSERR_SERVERFAULT,
180	0,
181};
182
183static short nfscberr_recall[] = {
184	NFSERR_RESOURCE,
185	NFSERR_BADHANDLE,
186	NFSERR_BADSTATEID,
187	NFSERR_BADXDR,
188	NFSERR_RESOURCE,
189	NFSERR_SERVERFAULT,
190	0,
191};
192
193static short *nfscl_cberrmap[] = {
194	nfscberr_null,
195	nfscberr_null,
196	nfscberr_null,
197	nfscberr_getattr,
198	nfscberr_recall
199};
200
201#define	NETFAMILY(clp) \
202		(((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET)
203
204/*
205 * Called for an open operation.
206 * If the nfhp argument is NULL, just get an openowner.
207 */
208APPLESTATIC int
209nfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg,
210    struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp,
211    struct nfsclopen **opp, int *newonep, int *retp, int lockit)
212{
213	struct nfsclclient *clp;
214	struct nfsclowner *owp, *nowp;
215	struct nfsclopen *op = NULL, *nop = NULL;
216	struct nfscldeleg *dp;
217	struct nfsclownerhead *ohp;
218	u_int8_t own[NFSV4CL_LOCKNAMELEN];
219	int ret;
220
221	if (newonep != NULL)
222		*newonep = 0;
223	if (opp != NULL)
224		*opp = NULL;
225	if (owpp != NULL)
226		*owpp = NULL;
227
228	/*
229	 * Might need one or both of these, so MALLOC them now, to
230	 * avoid a tsleep() in MALLOC later.
231	 */
232	MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner),
233	    M_NFSCLOWNER, M_WAITOK);
234	if (nfhp != NULL)
235	    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
236		fhlen - 1, M_NFSCLOPEN, M_WAITOK);
237	ret = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
238	if (ret != 0) {
239		FREE((caddr_t)nowp, M_NFSCLOWNER);
240		if (nop != NULL)
241			FREE((caddr_t)nop, M_NFSCLOPEN);
242		return (ret);
243	}
244
245	/*
246	 * Get the Open iff it already exists.
247	 * If none found, add the new one or return error, depending upon
248	 * "create".
249	 */
250	nfscl_filllockowner(p->td_proc, own, F_POSIX);
251	NFSLOCKCLSTATE();
252	dp = NULL;
253	/* First check the delegation list */
254	if (nfhp != NULL && usedeleg) {
255		LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
256			if (dp->nfsdl_fhlen == fhlen &&
257			    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
258				if (!(amode & NFSV4OPEN_ACCESSWRITE) ||
259				    (dp->nfsdl_flags & NFSCLDL_WRITE))
260					break;
261				dp = NULL;
262				break;
263			}
264		}
265	}
266
267	if (dp != NULL)
268		ohp = &dp->nfsdl_owner;
269	else
270		ohp = &clp->nfsc_owner;
271	/* Now, search for an openowner */
272	LIST_FOREACH(owp, ohp, nfsow_list) {
273		if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN))
274			break;
275	}
276
277	/*
278	 * Create a new open, as required.
279	 */
280	nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen,
281	    newonep);
282
283	/*
284	 * Now, check the mode on the open and return the appropriate
285	 * value.
286	 */
287	if (retp != NULL) {
288		if (nfhp != NULL && dp != NULL && nop == NULL)
289			/* new local open on delegation */
290			*retp = NFSCLOPEN_SETCRED;
291		else
292			*retp = NFSCLOPEN_OK;
293	}
294	if (op != NULL && (amode & ~(op->nfso_mode))) {
295		op->nfso_mode |= amode;
296		if (retp != NULL && dp == NULL)
297			*retp = NFSCLOPEN_DOOPEN;
298	}
299
300	/*
301	 * Serialize modifications to the open owner for multiple threads
302	 * within the same process using a read/write sleep lock.
303	 */
304	if (lockit)
305		nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
306	NFSUNLOCKCLSTATE();
307	if (nowp != NULL)
308		FREE((caddr_t)nowp, M_NFSCLOWNER);
309	if (nop != NULL)
310		FREE((caddr_t)nop, M_NFSCLOPEN);
311	if (owpp != NULL)
312		*owpp = owp;
313	if (opp != NULL)
314		*opp = op;
315	return (0);
316}
317
318/*
319 * Create a new open, as required.
320 */
321static void
322nfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp,
323    struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp,
324    struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen,
325    int *newonep)
326{
327	struct nfsclowner *owp = *owpp, *nowp;
328	struct nfsclopen *op, *nop;
329
330	if (nowpp != NULL)
331		nowp = *nowpp;
332	else
333		nowp = NULL;
334	if (nopp != NULL)
335		nop = *nopp;
336	else
337		nop = NULL;
338	if (owp == NULL && nowp != NULL) {
339		NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
340		LIST_INIT(&nowp->nfsow_open);
341		nowp->nfsow_clp = clp;
342		nowp->nfsow_seqid = 0;
343		nowp->nfsow_defunct = 0;
344		nfscl_lockinit(&nowp->nfsow_rwlock);
345		if (dp != NULL) {
346			newnfsstats.cllocalopenowners++;
347			LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list);
348		} else {
349			newnfsstats.clopenowners++;
350			LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list);
351		}
352		owp = *owpp = nowp;
353		*nowpp = NULL;
354		if (newonep != NULL)
355			*newonep = 1;
356	}
357
358	 /* If an fhp has been specified, create an Open as well. */
359	if (fhp != NULL) {
360		/* and look for the correct open, based upon FH */
361		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
362			if (op->nfso_fhlen == fhlen &&
363			    !NFSBCMP(op->nfso_fh, fhp, fhlen))
364				break;
365		}
366		if (op == NULL && nop != NULL) {
367			nop->nfso_own = owp;
368			nop->nfso_mode = 0;
369			nop->nfso_opencnt = 0;
370			nop->nfso_posixlock = 1;
371			nop->nfso_fhlen = fhlen;
372			NFSBCOPY(fhp, nop->nfso_fh, fhlen);
373			LIST_INIT(&nop->nfso_lock);
374			nop->nfso_stateid.seqid = 0;
375			nop->nfso_stateid.other[0] = 0;
376			nop->nfso_stateid.other[1] = 0;
377			nop->nfso_stateid.other[2] = 0;
378			if (dp != NULL) {
379				TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
380				TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
381				    nfsdl_list);
382				dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
383				newnfsstats.cllocalopens++;
384			} else {
385				newnfsstats.clopens++;
386			}
387			LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list);
388			*opp = nop;
389			*nopp = NULL;
390			if (newonep != NULL)
391				*newonep = 1;
392		} else {
393			*opp = op;
394		}
395	}
396}
397
398/*
399 * Called to find/add a delegation to a client.
400 */
401APPLESTATIC int
402nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
403    int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp)
404{
405	struct nfscldeleg *dp = *dpp, *tdp;
406
407	/*
408	 * First, if we have received a Read delegation for a file on a
409	 * read/write file system, just return it, because they aren't
410	 * useful, imho.
411	 */
412	if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) &&
413	    (dp->nfsdl_flags & NFSCLDL_READ)) {
414		(void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p);
415		FREE((caddr_t)dp, M_NFSCLDELEG);
416		*dpp = NULL;
417		return (0);
418	}
419
420	/* Look for the correct deleg, based upon FH */
421	NFSLOCKCLSTATE();
422	tdp = nfscl_finddeleg(clp, nfhp, fhlen);
423	if (tdp == NULL) {
424		if (dp == NULL) {
425			NFSUNLOCKCLSTATE();
426			return (NFSERR_BADSTATEID);
427		}
428		*dpp = NULL;
429		TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
430		LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp,
431		    nfsdl_hash);
432		dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
433		newnfsstats.cldelegates++;
434		nfscl_delegcnt++;
435	} else {
436		/*
437		 * Delegation already exists, what do we do if a new one??
438		 */
439		if (dp != NULL) {
440			printf("Deleg already exists!\n");
441			FREE((caddr_t)dp, M_NFSCLDELEG);
442			*dpp = NULL;
443		} else {
444			*dpp = tdp;
445		}
446	}
447	NFSUNLOCKCLSTATE();
448	return (0);
449}
450
451/*
452 * Find a delegation for this file handle. Return NULL upon failure.
453 */
454static struct nfscldeleg *
455nfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
456{
457	struct nfscldeleg *dp;
458
459	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) {
460	    if (dp->nfsdl_fhlen == fhlen &&
461		!NFSBCMP(dp->nfsdl_fh, fhp, fhlen))
462		break;
463	}
464	return (dp);
465}
466
467/*
468 * Get a stateid for an I/O operation. First, look for an open and iff
469 * found, return either a lockowner stateid or the open stateid.
470 * If no Open is found, just return error and the special stateid of all zeros.
471 */
472APPLESTATIC int
473nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
474    int fords, struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp,
475    void **lckpp)
476{
477	struct nfsclclient *clp;
478	struct nfsclowner *owp;
479	struct nfsclopen *op = NULL;
480	struct nfscllockowner *lp;
481	struct nfscldeleg *dp;
482	struct nfsnode *np;
483	u_int8_t own[NFSV4CL_LOCKNAMELEN];
484	int error, done;
485
486	*lckpp = NULL;
487	/*
488	 * Initially, just set the special stateid of all zeros.
489	 * (Don't do this for a DS, since the special stateid can't be used.)
490	 */
491	if (fords == 0) {
492		stateidp->seqid = 0;
493		stateidp->other[0] = 0;
494		stateidp->other[1] = 0;
495		stateidp->other[2] = 0;
496	}
497	if (vnode_vtype(vp) != VREG)
498		return (EISDIR);
499	np = VTONFS(vp);
500	NFSLOCKCLSTATE();
501	clp = nfscl_findcl(VFSTONFS(vnode_mount(vp)));
502	if (clp == NULL) {
503		NFSUNLOCKCLSTATE();
504		return (EACCES);
505	}
506
507	/*
508	 * Wait for recovery to complete.
509	 */
510	while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG))
511		(void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR,
512		    PZERO, "nfsrecvr", NULL);
513
514	/*
515	 * First, look for a delegation.
516	 */
517	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
518		if (dp->nfsdl_fhlen == fhlen &&
519		    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
520			if (!(mode & NFSV4OPEN_ACCESSWRITE) ||
521			    (dp->nfsdl_flags & NFSCLDL_WRITE)) {
522				stateidp->seqid = dp->nfsdl_stateid.seqid;
523				stateidp->other[0] = dp->nfsdl_stateid.other[0];
524				stateidp->other[1] = dp->nfsdl_stateid.other[1];
525				stateidp->other[2] = dp->nfsdl_stateid.other[2];
526				if (!(np->n_flag & NDELEGRECALL)) {
527					TAILQ_REMOVE(&clp->nfsc_deleg, dp,
528					    nfsdl_list);
529					TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
530					    nfsdl_list);
531					dp->nfsdl_timestamp = NFSD_MONOSEC +
532					    120;
533					dp->nfsdl_rwlock.nfslock_usecnt++;
534					*lckpp = (void *)&dp->nfsdl_rwlock;
535				}
536				NFSUNLOCKCLSTATE();
537				return (0);
538			}
539			break;
540		}
541	}
542
543	if (p != NULL) {
544		/*
545		 * If p != NULL, we want to search the parentage tree
546		 * for a matching OpenOwner and use that.
547		 */
548		nfscl_filllockowner(p->td_proc, own, F_POSIX);
549		lp = NULL;
550		error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, own, own,
551		    mode, &lp, &op);
552		if (error == 0 && lp != NULL && fords == 0) {
553			/* Don't return a lock stateid for a DS. */
554			stateidp->seqid =
555			    lp->nfsl_stateid.seqid;
556			stateidp->other[0] =
557			    lp->nfsl_stateid.other[0];
558			stateidp->other[1] =
559			    lp->nfsl_stateid.other[1];
560			stateidp->other[2] =
561			    lp->nfsl_stateid.other[2];
562			NFSUNLOCKCLSTATE();
563			return (0);
564		}
565	}
566	if (op == NULL) {
567		/* If not found, just look for any OpenOwner that will work. */
568		done = 0;
569		owp = LIST_FIRST(&clp->nfsc_owner);
570		while (!done && owp != NULL) {
571			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
572				if (op->nfso_fhlen == fhlen &&
573				    !NFSBCMP(op->nfso_fh, nfhp, fhlen) &&
574				    (mode & op->nfso_mode) == mode) {
575					done = 1;
576					break;
577				}
578			}
579			if (!done)
580				owp = LIST_NEXT(owp, nfsow_list);
581		}
582		if (!done) {
583			NFSUNLOCKCLSTATE();
584			return (ENOENT);
585		}
586		/*
587		 * For read aheads or write behinds, use the open cred.
588		 * A read ahead or write behind is indicated by p == NULL.
589		 */
590		if (p == NULL)
591			newnfs_copycred(&op->nfso_cred, cred);
592	}
593
594	/*
595	 * No lock stateid, so return the open stateid.
596	 */
597	stateidp->seqid = op->nfso_stateid.seqid;
598	stateidp->other[0] = op->nfso_stateid.other[0];
599	stateidp->other[1] = op->nfso_stateid.other[1];
600	stateidp->other[2] = op->nfso_stateid.other[2];
601	NFSUNLOCKCLSTATE();
602	return (0);
603}
604
605/*
606 * Search for a matching file, mode and, optionally, lockowner.
607 */
608static int
609nfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen,
610    u_int8_t *openown, u_int8_t *lockown, u_int32_t mode,
611    struct nfscllockowner **lpp, struct nfsclopen **opp)
612{
613	struct nfsclowner *owp;
614	struct nfsclopen *op, *rop, *rop2;
615	struct nfscllockowner *lp;
616	int keep_looping;
617
618	if (lpp != NULL)
619		*lpp = NULL;
620	/*
621	 * rop will be set to the open to be returned. There are three
622	 * variants of this, all for an open of the correct file:
623	 * 1 - A match of lockown.
624	 * 2 - A match of the openown, when no lockown match exists.
625	 * 3 - A match for any open, if no openown or lockown match exists.
626	 * Looking for #2 over #3 probably isn't necessary, but since
627	 * RFC3530 is vague w.r.t. the relationship between openowners and
628	 * lockowners, I think this is the safer way to go.
629	 */
630	rop = NULL;
631	rop2 = NULL;
632	keep_looping = 1;
633	/* Search the client list */
634	owp = LIST_FIRST(ohp);
635	while (owp != NULL && keep_looping != 0) {
636		/* and look for the correct open */
637		op = LIST_FIRST(&owp->nfsow_open);
638		while (op != NULL && keep_looping != 0) {
639			if (op->nfso_fhlen == fhlen &&
640			    !NFSBCMP(op->nfso_fh, nfhp, fhlen)
641			    && (op->nfso_mode & mode) == mode) {
642				if (lpp != NULL) {
643					/* Now look for a matching lockowner. */
644					LIST_FOREACH(lp, &op->nfso_lock,
645					    nfsl_list) {
646						if (!NFSBCMP(lp->nfsl_owner,
647						    lockown,
648						    NFSV4CL_LOCKNAMELEN)) {
649							*lpp = lp;
650							rop = op;
651							keep_looping = 0;
652							break;
653						}
654					}
655				}
656				if (rop == NULL && !NFSBCMP(owp->nfsow_owner,
657				    openown, NFSV4CL_LOCKNAMELEN)) {
658					rop = op;
659					if (lpp == NULL)
660						keep_looping = 0;
661				}
662				if (rop2 == NULL)
663					rop2 = op;
664			}
665			op = LIST_NEXT(op, nfso_list);
666		}
667		owp = LIST_NEXT(owp, nfsow_list);
668	}
669	if (rop == NULL)
670		rop = rop2;
671	if (rop == NULL)
672		return (EBADF);
673	*opp = rop;
674	return (0);
675}
676
677/*
678 * Release use of an open owner. Called when open operations are done
679 * with the open owner.
680 */
681APPLESTATIC void
682nfscl_ownerrelease(struct nfsclowner *owp, __unused int error,
683    __unused int candelete, int unlocked)
684{
685
686	if (owp == NULL)
687		return;
688	NFSLOCKCLSTATE();
689	if (!unlocked)
690		nfscl_lockunlock(&owp->nfsow_rwlock);
691	nfscl_clrelease(owp->nfsow_clp);
692	NFSUNLOCKCLSTATE();
693}
694
695/*
696 * Release use of an open structure under an open owner.
697 */
698APPLESTATIC void
699nfscl_openrelease(struct nfsclopen *op, int error, int candelete)
700{
701	struct nfsclclient *clp;
702	struct nfsclowner *owp;
703
704	if (op == NULL)
705		return;
706	NFSLOCKCLSTATE();
707	owp = op->nfso_own;
708	nfscl_lockunlock(&owp->nfsow_rwlock);
709	clp = owp->nfsow_clp;
710	if (error && candelete && op->nfso_opencnt == 0)
711		nfscl_freeopen(op, 0);
712	nfscl_clrelease(clp);
713	NFSUNLOCKCLSTATE();
714}
715
716/*
717 * Called to get a clientid structure. It will optionally lock the
718 * client data structures to do the SetClientId/SetClientId_confirm,
719 * but will release that lock and return the clientid with a refernce
720 * count on it.
721 * If the "cred" argument is NULL, a new clientid should not be created.
722 * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot
723 * be done.
724 * The start_renewthread argument tells nfscl_getcl() to start a renew
725 * thread if this creates a new clp.
726 * It always clpp with a reference count on it, unless returning an error.
727 */
728APPLESTATIC int
729nfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
730    int start_renewthread, struct nfsclclient **clpp)
731{
732	struct nfsclclient *clp;
733	struct nfsclclient *newclp = NULL;
734	struct nfsmount *nmp;
735	char uuid[HOSTUUIDLEN];
736	int igotlock = 0, error, trystalecnt, clidinusedelay, i;
737	u_int16_t idlen = 0;
738
739	nmp = VFSTONFS(mp);
740	if (cred != NULL) {
741		getcredhostuuid(cred, uuid, sizeof uuid);
742		idlen = strlen(uuid);
743		if (idlen > 0)
744			idlen += sizeof (u_int64_t);
745		else
746			idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
747		MALLOC(newclp, struct nfsclclient *,
748		    sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
749		    M_WAITOK | M_ZERO);
750	}
751	NFSLOCKCLSTATE();
752	/*
753	 * If a forced dismount is already in progress, don't
754	 * allocate a new clientid and get out now. For the case where
755	 * clp != NULL, this is a harmless optimization.
756	 */
757	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
758		NFSUNLOCKCLSTATE();
759		if (newclp != NULL)
760			free(newclp, M_NFSCLCLIENT);
761		return (EBADF);
762	}
763	clp = nmp->nm_clp;
764	if (clp == NULL) {
765		if (newclp == NULL) {
766			NFSUNLOCKCLSTATE();
767			return (EACCES);
768		}
769		clp = newclp;
770		clp->nfsc_idlen = idlen;
771		LIST_INIT(&clp->nfsc_owner);
772		TAILQ_INIT(&clp->nfsc_deleg);
773		TAILQ_INIT(&clp->nfsc_layout);
774		LIST_INIT(&clp->nfsc_devinfo);
775		for (i = 0; i < NFSCLDELEGHASHSIZE; i++)
776			LIST_INIT(&clp->nfsc_deleghash[i]);
777		for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++)
778			LIST_INIT(&clp->nfsc_layouthash[i]);
779		clp->nfsc_flags = NFSCLFLAGS_INITED;
780		clp->nfsc_clientidrev = 1;
781		clp->nfsc_cbident = nfscl_nextcbident();
782		nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id,
783		    clp->nfsc_idlen);
784		LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list);
785		nmp->nm_clp = clp;
786		clp->nfsc_nmp = nmp;
787		NFSUNLOCKCLSTATE();
788		if (start_renewthread != 0)
789			nfscl_start_renewthread(clp);
790	} else {
791		NFSUNLOCKCLSTATE();
792		if (newclp != NULL)
793			free(newclp, M_NFSCLCLIENT);
794	}
795	NFSLOCKCLSTATE();
796	while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock &&
797	    (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0)
798		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
799		    NFSCLSTATEMUTEXPTR, mp);
800	if (!igotlock)
801		nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
802	if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
803		/*
804		 * Both nfsv4_lock() and nfsv4_getref() know to check
805		 * for MNTK_UNMOUNTF and return without sleeping to
806		 * wait for the exclusive lock to be released, since it
807		 * might be held by nfscl_umount() and we need to get out
808		 * now for that case and not wait until nfscl_umount()
809		 * releases it.
810		 */
811		NFSUNLOCKCLSTATE();
812		return (EBADF);
813	}
814	NFSUNLOCKCLSTATE();
815
816	/*
817	 * If it needs a clientid, do the setclientid now.
818	 */
819	if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) {
820		if (!igotlock)
821			panic("nfscl_clget");
822		if (p == NULL || cred == NULL) {
823			NFSLOCKCLSTATE();
824			nfsv4_unlock(&clp->nfsc_lock, 0);
825			NFSUNLOCKCLSTATE();
826			return (EACCES);
827		}
828		/*
829		 * If RFC3530 Sec. 14.2.33 is taken literally,
830		 * NFSERR_CLIDINUSE will be returned persistently for the
831		 * case where a new mount of the same file system is using
832		 * a different principal. In practice, NFSERR_CLIDINUSE is
833		 * only returned when there is outstanding unexpired state
834		 * on the clientid. As such, try for twice the lease
835		 * interval, if we know what that is. Otherwise, make a
836		 * wild ass guess.
837		 * The case of returning NFSERR_STALECLIENTID is far less
838		 * likely, but might occur if there is a significant delay
839		 * between doing the SetClientID and SetClientIDConfirm Ops,
840		 * such that the server throws away the clientid before
841		 * receiving the SetClientIDConfirm.
842		 */
843		if (clp->nfsc_renew > 0)
844			clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
845		else
846			clidinusedelay = 120;
847		trystalecnt = 3;
848		do {
849			error = nfsrpc_setclient(nmp, clp, 0, cred, p);
850			if (error == NFSERR_STALECLIENTID ||
851			    error == NFSERR_STALEDONTRECOVER ||
852			    error == NFSERR_BADSESSION ||
853			    error == NFSERR_CLIDINUSE) {
854				(void) nfs_catnap(PZERO, error, "nfs_setcl");
855			}
856		} while (((error == NFSERR_STALECLIENTID ||
857		     error == NFSERR_BADSESSION ||
858		     error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) ||
859		    (error == NFSERR_CLIDINUSE && --clidinusedelay > 0));
860		if (error) {
861			NFSLOCKCLSTATE();
862			nfsv4_unlock(&clp->nfsc_lock, 0);
863			NFSUNLOCKCLSTATE();
864			return (error);
865		}
866		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
867	}
868	if (igotlock) {
869		NFSLOCKCLSTATE();
870		nfsv4_unlock(&clp->nfsc_lock, 1);
871		NFSUNLOCKCLSTATE();
872	}
873
874	*clpp = clp;
875	return (0);
876}
877
878/*
879 * Get a reference to a clientid and return it, if valid.
880 */
881APPLESTATIC struct nfsclclient *
882nfscl_findcl(struct nfsmount *nmp)
883{
884	struct nfsclclient *clp;
885
886	clp = nmp->nm_clp;
887	if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID))
888		return (NULL);
889	return (clp);
890}
891
892/*
893 * Release the clientid structure. It may be locked or reference counted.
894 */
895static void
896nfscl_clrelease(struct nfsclclient *clp)
897{
898
899	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
900		nfsv4_unlock(&clp->nfsc_lock, 0);
901	else
902		nfsv4_relref(&clp->nfsc_lock);
903}
904
905/*
906 * External call for nfscl_clrelease.
907 */
908APPLESTATIC void
909nfscl_clientrelease(struct nfsclclient *clp)
910{
911
912	NFSLOCKCLSTATE();
913	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
914		nfsv4_unlock(&clp->nfsc_lock, 0);
915	else
916		nfsv4_relref(&clp->nfsc_lock);
917	NFSUNLOCKCLSTATE();
918}
919
920/*
921 * Called when wanting to lock a byte region.
922 */
923APPLESTATIC int
924nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
925    short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp,
926    int recovery, void *id, int flags, u_int8_t *rownp, u_int8_t *ropenownp,
927    struct nfscllockowner **lpp, int *newonep, int *donelocallyp)
928{
929	struct nfscllockowner *lp;
930	struct nfsclopen *op;
931	struct nfsclclient *clp;
932	struct nfscllockowner *nlp;
933	struct nfscllock *nlop, *otherlop;
934	struct nfscldeleg *dp = NULL, *ldp = NULL;
935	struct nfscllockownerhead *lhp = NULL;
936	struct nfsnode *np;
937	u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp, openown[NFSV4CL_LOCKNAMELEN];
938	u_int8_t *openownp;
939	int error = 0, ret, donelocally = 0;
940	u_int32_t mode;
941
942	/* For Lock Ops, the open mode doesn't matter, so use 0 to match any. */
943	mode = 0;
944	np = VTONFS(vp);
945	*lpp = NULL;
946	lp = NULL;
947	*newonep = 0;
948	*donelocallyp = 0;
949
950	/*
951	 * Might need these, so MALLOC them now, to
952	 * avoid a tsleep() in MALLOC later.
953	 */
954	MALLOC(nlp, struct nfscllockowner *,
955	    sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK);
956	MALLOC(otherlop, struct nfscllock *,
957	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
958	MALLOC(nlop, struct nfscllock *,
959	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
960	nlop->nfslo_type = type;
961	nlop->nfslo_first = off;
962	if (len == NFS64BITSSET) {
963		nlop->nfslo_end = NFS64BITSSET;
964	} else {
965		nlop->nfslo_end = off + len;
966		if (nlop->nfslo_end <= nlop->nfslo_first)
967			error = NFSERR_INVAL;
968	}
969
970	if (!error) {
971		if (recovery)
972			clp = rclp;
973		else
974			error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
975	}
976	if (error) {
977		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
978		FREE((caddr_t)otherlop, M_NFSCLLOCK);
979		FREE((caddr_t)nlop, M_NFSCLLOCK);
980		return (error);
981	}
982
983	op = NULL;
984	if (recovery) {
985		ownp = rownp;
986		openownp = ropenownp;
987	} else {
988		nfscl_filllockowner(id, own, flags);
989		ownp = own;
990		nfscl_filllockowner(p->td_proc, openown, F_POSIX);
991		openownp = openown;
992	}
993	if (!recovery) {
994		NFSLOCKCLSTATE();
995		/*
996		 * First, search for a delegation. If one exists for this file,
997		 * the lock can be done locally against it, so long as there
998		 * isn't a local lock conflict.
999		 */
1000		ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1001		    np->n_fhp->nfh_len);
1002		/* Just sanity check for correct type of delegation */
1003		if (dp != NULL && ((dp->nfsdl_flags &
1004		    (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 ||
1005		     (type == F_WRLCK &&
1006		      (dp->nfsdl_flags & NFSCLDL_WRITE) == 0)))
1007			dp = NULL;
1008	}
1009	if (dp != NULL) {
1010		/* Now, find an open and maybe a lockowner. */
1011		ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh,
1012		    np->n_fhp->nfh_len, openownp, ownp, mode, NULL, &op);
1013		if (ret)
1014			ret = nfscl_getopen(&clp->nfsc_owner,
1015			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1016			    ownp, mode, NULL, &op);
1017		if (!ret) {
1018			lhp = &dp->nfsdl_lock;
1019			TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
1020			TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
1021			dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
1022			donelocally = 1;
1023		} else {
1024			dp = NULL;
1025		}
1026	}
1027	if (!donelocally) {
1028		/*
1029		 * Get the related Open and maybe lockowner.
1030		 */
1031		error = nfscl_getopen(&clp->nfsc_owner,
1032		    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
1033		    ownp, mode, &lp, &op);
1034		if (!error)
1035			lhp = &op->nfso_lock;
1036	}
1037	if (!error && !recovery)
1038		error = nfscl_localconflict(clp, np->n_fhp->nfh_fh,
1039		    np->n_fhp->nfh_len, nlop, ownp, ldp, NULL);
1040	if (error) {
1041		if (!recovery) {
1042			nfscl_clrelease(clp);
1043			NFSUNLOCKCLSTATE();
1044		}
1045		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1046		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1047		FREE((caddr_t)nlop, M_NFSCLLOCK);
1048		return (error);
1049	}
1050
1051	/*
1052	 * Ok, see if a lockowner exists and create one, as required.
1053	 */
1054	if (lp == NULL)
1055		LIST_FOREACH(lp, lhp, nfsl_list) {
1056			if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
1057				break;
1058		}
1059	if (lp == NULL) {
1060		NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
1061		if (recovery)
1062			NFSBCOPY(ropenownp, nlp->nfsl_openowner,
1063			    NFSV4CL_LOCKNAMELEN);
1064		else
1065			NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner,
1066			    NFSV4CL_LOCKNAMELEN);
1067		nlp->nfsl_seqid = 0;
1068		nlp->nfsl_lockflags = flags;
1069		nlp->nfsl_inprog = NULL;
1070		nfscl_lockinit(&nlp->nfsl_rwlock);
1071		LIST_INIT(&nlp->nfsl_lock);
1072		if (donelocally) {
1073			nlp->nfsl_open = NULL;
1074			newnfsstats.cllocallockowners++;
1075		} else {
1076			nlp->nfsl_open = op;
1077			newnfsstats.cllockowners++;
1078		}
1079		LIST_INSERT_HEAD(lhp, nlp, nfsl_list);
1080		lp = nlp;
1081		nlp = NULL;
1082		*newonep = 1;
1083	}
1084
1085	/*
1086	 * Now, update the byte ranges for locks.
1087	 */
1088	ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally);
1089	if (!ret)
1090		donelocally = 1;
1091	if (donelocally) {
1092		*donelocallyp = 1;
1093		if (!recovery)
1094			nfscl_clrelease(clp);
1095	} else {
1096		/*
1097		 * Serial modifications on the lock owner for multiple threads
1098		 * for the same process using a read/write lock.
1099		 */
1100		if (!recovery)
1101			nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1102	}
1103	if (!recovery)
1104		NFSUNLOCKCLSTATE();
1105
1106	if (nlp)
1107		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1108	if (nlop)
1109		FREE((caddr_t)nlop, M_NFSCLLOCK);
1110	if (otherlop)
1111		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1112
1113	*lpp = lp;
1114	return (0);
1115}
1116
1117/*
1118 * Called to unlock a byte range, for LockU.
1119 */
1120APPLESTATIC int
1121nfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
1122    __unused struct ucred *cred, NFSPROC_T *p, int callcnt,
1123    struct nfsclclient *clp, void *id, int flags,
1124    struct nfscllockowner **lpp, int *dorpcp)
1125{
1126	struct nfscllockowner *lp;
1127	struct nfsclowner *owp;
1128	struct nfsclopen *op;
1129	struct nfscllock *nlop, *other_lop = NULL;
1130	struct nfscldeleg *dp;
1131	struct nfsnode *np;
1132	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1133	int ret = 0, fnd;
1134
1135	np = VTONFS(vp);
1136	*lpp = NULL;
1137	*dorpcp = 0;
1138
1139	/*
1140	 * Might need these, so MALLOC them now, to
1141	 * avoid a tsleep() in MALLOC later.
1142	 */
1143	MALLOC(nlop, struct nfscllock *,
1144	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1145	nlop->nfslo_type = F_UNLCK;
1146	nlop->nfslo_first = off;
1147	if (len == NFS64BITSSET) {
1148		nlop->nfslo_end = NFS64BITSSET;
1149	} else {
1150		nlop->nfslo_end = off + len;
1151		if (nlop->nfslo_end <= nlop->nfslo_first) {
1152			FREE((caddr_t)nlop, M_NFSCLLOCK);
1153			return (NFSERR_INVAL);
1154		}
1155	}
1156	if (callcnt == 0) {
1157		MALLOC(other_lop, struct nfscllock *,
1158		    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1159		*other_lop = *nlop;
1160	}
1161	nfscl_filllockowner(id, own, flags);
1162	dp = NULL;
1163	NFSLOCKCLSTATE();
1164	if (callcnt == 0)
1165		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1166		    np->n_fhp->nfh_len);
1167
1168	/*
1169	 * First, unlock any local regions on a delegation.
1170	 */
1171	if (dp != NULL) {
1172		/* Look for this lockowner. */
1173		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1174			if (!NFSBCMP(lp->nfsl_owner, own,
1175			    NFSV4CL_LOCKNAMELEN))
1176				break;
1177		}
1178		if (lp != NULL)
1179			/* Use other_lop, so nlop is still available */
1180			(void)nfscl_updatelock(lp, &other_lop, NULL, 1);
1181	}
1182
1183	/*
1184	 * Now, find a matching open/lockowner that hasn't already been done,
1185	 * as marked by nfsl_inprog.
1186	 */
1187	lp = NULL;
1188	fnd = 0;
1189	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1190	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1191		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1192		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1193		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1194			if (lp->nfsl_inprog == NULL &&
1195			    !NFSBCMP(lp->nfsl_owner, own,
1196			     NFSV4CL_LOCKNAMELEN)) {
1197				fnd = 1;
1198				break;
1199			}
1200		    }
1201		    if (fnd)
1202			break;
1203		}
1204	    }
1205	    if (fnd)
1206		break;
1207	}
1208
1209	if (lp != NULL) {
1210		ret = nfscl_updatelock(lp, &nlop, NULL, 0);
1211		if (ret)
1212			*dorpcp = 1;
1213		/*
1214		 * Serial modifications on the lock owner for multiple
1215		 * threads for the same process using a read/write lock.
1216		 */
1217		lp->nfsl_inprog = p;
1218		nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1219		*lpp = lp;
1220	}
1221	NFSUNLOCKCLSTATE();
1222	if (nlop)
1223		FREE((caddr_t)nlop, M_NFSCLLOCK);
1224	if (other_lop)
1225		FREE((caddr_t)other_lop, M_NFSCLLOCK);
1226	return (0);
1227}
1228
1229/*
1230 * Release all lockowners marked in progess for this process and file.
1231 */
1232APPLESTATIC void
1233nfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p,
1234    void *id, int flags)
1235{
1236	struct nfsclowner *owp;
1237	struct nfsclopen *op;
1238	struct nfscllockowner *lp;
1239	struct nfsnode *np;
1240	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1241
1242	np = VTONFS(vp);
1243	nfscl_filllockowner(id, own, flags);
1244	NFSLOCKCLSTATE();
1245	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1246	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1247		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1248		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1249		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1250			if (lp->nfsl_inprog == p &&
1251			    !NFSBCMP(lp->nfsl_owner, own,
1252			    NFSV4CL_LOCKNAMELEN)) {
1253			    lp->nfsl_inprog = NULL;
1254			    nfscl_lockunlock(&lp->nfsl_rwlock);
1255			}
1256		    }
1257		}
1258	    }
1259	}
1260	nfscl_clrelease(clp);
1261	NFSUNLOCKCLSTATE();
1262}
1263
1264/*
1265 * Called to find out if any bytes within the byte range specified are
1266 * write locked by the calling process. Used to determine if flushing
1267 * is required before a LockU.
1268 * If in doubt, return 1, so the flush will occur.
1269 */
1270APPLESTATIC int
1271nfscl_checkwritelocked(vnode_t vp, struct flock *fl,
1272    struct ucred *cred, NFSPROC_T *p, void *id, int flags)
1273{
1274	struct nfsclowner *owp;
1275	struct nfscllockowner *lp;
1276	struct nfsclopen *op;
1277	struct nfsclclient *clp;
1278	struct nfscllock *lop;
1279	struct nfscldeleg *dp;
1280	struct nfsnode *np;
1281	u_int64_t off, end;
1282	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1283	int error = 0;
1284
1285	np = VTONFS(vp);
1286	switch (fl->l_whence) {
1287	case SEEK_SET:
1288	case SEEK_CUR:
1289		/*
1290		 * Caller is responsible for adding any necessary offset
1291		 * when SEEK_CUR is used.
1292		 */
1293		off = fl->l_start;
1294		break;
1295	case SEEK_END:
1296		off = np->n_size + fl->l_start;
1297		break;
1298	default:
1299		return (1);
1300	};
1301	if (fl->l_len != 0) {
1302		end = off + fl->l_len;
1303		if (end < off)
1304			return (1);
1305	} else {
1306		end = NFS64BITSSET;
1307	}
1308
1309	error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
1310	if (error)
1311		return (1);
1312	nfscl_filllockowner(id, own, flags);
1313	NFSLOCKCLSTATE();
1314
1315	/*
1316	 * First check the delegation locks.
1317	 */
1318	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
1319	if (dp != NULL) {
1320		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1321			if (!NFSBCMP(lp->nfsl_owner, own,
1322			    NFSV4CL_LOCKNAMELEN))
1323				break;
1324		}
1325		if (lp != NULL) {
1326			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1327				if (lop->nfslo_first >= end)
1328					break;
1329				if (lop->nfslo_end <= off)
1330					continue;
1331				if (lop->nfslo_type == F_WRLCK) {
1332					nfscl_clrelease(clp);
1333					NFSUNLOCKCLSTATE();
1334					return (1);
1335				}
1336			}
1337		}
1338	}
1339
1340	/*
1341	 * Now, check state against the server.
1342	 */
1343	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1344	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1345		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1346		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1347		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1348			if (!NFSBCMP(lp->nfsl_owner, own,
1349			    NFSV4CL_LOCKNAMELEN))
1350			    break;
1351		    }
1352		    if (lp != NULL) {
1353			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1354			    if (lop->nfslo_first >= end)
1355				break;
1356			    if (lop->nfslo_end <= off)
1357				continue;
1358			    if (lop->nfslo_type == F_WRLCK) {
1359				nfscl_clrelease(clp);
1360				NFSUNLOCKCLSTATE();
1361				return (1);
1362			    }
1363			}
1364		    }
1365		}
1366	    }
1367	}
1368	nfscl_clrelease(clp);
1369	NFSUNLOCKCLSTATE();
1370	return (0);
1371}
1372
1373/*
1374 * Release a byte range lock owner structure.
1375 */
1376APPLESTATIC void
1377nfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete)
1378{
1379	struct nfsclclient *clp;
1380
1381	if (lp == NULL)
1382		return;
1383	NFSLOCKCLSTATE();
1384	clp = lp->nfsl_open->nfso_own->nfsow_clp;
1385	if (error != 0 && candelete &&
1386	    (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0)
1387		nfscl_freelockowner(lp, 0);
1388	else
1389		nfscl_lockunlock(&lp->nfsl_rwlock);
1390	nfscl_clrelease(clp);
1391	NFSUNLOCKCLSTATE();
1392}
1393
1394/*
1395 * Free up an open structure and any associated byte range lock structures.
1396 */
1397APPLESTATIC void
1398nfscl_freeopen(struct nfsclopen *op, int local)
1399{
1400
1401	LIST_REMOVE(op, nfso_list);
1402	nfscl_freealllocks(&op->nfso_lock, local);
1403	FREE((caddr_t)op, M_NFSCLOPEN);
1404	if (local)
1405		newnfsstats.cllocalopens--;
1406	else
1407		newnfsstats.clopens--;
1408}
1409
1410/*
1411 * Free up all lock owners and associated locks.
1412 */
1413static void
1414nfscl_freealllocks(struct nfscllockownerhead *lhp, int local)
1415{
1416	struct nfscllockowner *lp, *nlp;
1417
1418	LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) {
1419		if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1420			panic("nfscllckw");
1421		nfscl_freelockowner(lp, local);
1422	}
1423}
1424
1425/*
1426 * Called for an Open when NFSERR_EXPIRED is received from the server.
1427 * If there are no byte range locks nor a Share Deny lost, try to do a
1428 * fresh Open. Otherwise, free the open.
1429 */
1430static int
1431nfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op,
1432    struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
1433{
1434	struct nfscllockowner *lp;
1435	struct nfscldeleg *dp;
1436	int mustdelete = 0, error;
1437
1438	/*
1439	 * Look for any byte range lock(s).
1440	 */
1441	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1442		if (!LIST_EMPTY(&lp->nfsl_lock)) {
1443			mustdelete = 1;
1444			break;
1445		}
1446	}
1447
1448	/*
1449	 * If no byte range lock(s) nor a Share deny, try to re-open.
1450	 */
1451	if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) {
1452		newnfs_copycred(&op->nfso_cred, cred);
1453		dp = NULL;
1454		error = nfsrpc_reopen(nmp, op->nfso_fh,
1455		    op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p);
1456		if (error) {
1457			mustdelete = 1;
1458			if (dp != NULL) {
1459				FREE((caddr_t)dp, M_NFSCLDELEG);
1460				dp = NULL;
1461			}
1462		}
1463		if (dp != NULL)
1464			nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh,
1465			    op->nfso_fhlen, cred, p, &dp);
1466	}
1467
1468	/*
1469	 * If a byte range lock or Share deny or couldn't re-open, free it.
1470	 */
1471	if (mustdelete)
1472		nfscl_freeopen(op, 0);
1473	return (mustdelete);
1474}
1475
1476/*
1477 * Free up an open owner structure.
1478 */
1479static void
1480nfscl_freeopenowner(struct nfsclowner *owp, int local)
1481{
1482
1483	LIST_REMOVE(owp, nfsow_list);
1484	FREE((caddr_t)owp, M_NFSCLOWNER);
1485	if (local)
1486		newnfsstats.cllocalopenowners--;
1487	else
1488		newnfsstats.clopenowners--;
1489}
1490
1491/*
1492 * Free up a byte range lock owner structure.
1493 */
1494APPLESTATIC void
1495nfscl_freelockowner(struct nfscllockowner *lp, int local)
1496{
1497	struct nfscllock *lop, *nlop;
1498
1499	LIST_REMOVE(lp, nfsl_list);
1500	LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
1501		nfscl_freelock(lop, local);
1502	}
1503	FREE((caddr_t)lp, M_NFSCLLOCKOWNER);
1504	if (local)
1505		newnfsstats.cllocallockowners--;
1506	else
1507		newnfsstats.cllockowners--;
1508}
1509
1510/*
1511 * Free up a byte range lock structure.
1512 */
1513APPLESTATIC void
1514nfscl_freelock(struct nfscllock *lop, int local)
1515{
1516
1517	LIST_REMOVE(lop, nfslo_list);
1518	FREE((caddr_t)lop, M_NFSCLLOCK);
1519	if (local)
1520		newnfsstats.cllocallocks--;
1521	else
1522		newnfsstats.cllocks--;
1523}
1524
1525/*
1526 * Clean out the state related to a delegation.
1527 */
1528static void
1529nfscl_cleandeleg(struct nfscldeleg *dp)
1530{
1531	struct nfsclowner *owp, *nowp;
1532	struct nfsclopen *op;
1533
1534	LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
1535		op = LIST_FIRST(&owp->nfsow_open);
1536		if (op != NULL) {
1537			if (LIST_NEXT(op, nfso_list) != NULL)
1538				panic("nfscleandel");
1539			nfscl_freeopen(op, 1);
1540		}
1541		nfscl_freeopenowner(owp, 1);
1542	}
1543	nfscl_freealllocks(&dp->nfsdl_lock, 1);
1544}
1545
1546/*
1547 * Free a delegation.
1548 */
1549static void
1550nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp)
1551{
1552
1553	TAILQ_REMOVE(hdp, dp, nfsdl_list);
1554	LIST_REMOVE(dp, nfsdl_hash);
1555	FREE((caddr_t)dp, M_NFSCLDELEG);
1556	newnfsstats.cldelegates--;
1557	nfscl_delegcnt--;
1558}
1559
1560/*
1561 * Free up all state related to this client structure.
1562 */
1563static void
1564nfscl_cleanclient(struct nfsclclient *clp)
1565{
1566	struct nfsclowner *owp, *nowp;
1567	struct nfsclopen *op, *nop;
1568
1569	/* Now, all the OpenOwners, etc. */
1570	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1571		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1572			nfscl_freeopen(op, 0);
1573		}
1574		nfscl_freeopenowner(owp, 0);
1575	}
1576}
1577
1578/*
1579 * Called when an NFSERR_EXPIRED is received from the server.
1580 */
1581static void
1582nfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
1583    struct ucred *cred, NFSPROC_T *p)
1584{
1585	struct nfsclowner *owp, *nowp, *towp;
1586	struct nfsclopen *op, *nop, *top;
1587	struct nfscldeleg *dp, *ndp;
1588	int ret, printed = 0;
1589
1590	/*
1591	 * First, merge locally issued Opens into the list for the server.
1592	 */
1593	dp = TAILQ_FIRST(&clp->nfsc_deleg);
1594	while (dp != NULL) {
1595	    ndp = TAILQ_NEXT(dp, nfsdl_list);
1596	    owp = LIST_FIRST(&dp->nfsdl_owner);
1597	    while (owp != NULL) {
1598		nowp = LIST_NEXT(owp, nfsow_list);
1599		op = LIST_FIRST(&owp->nfsow_open);
1600		if (op != NULL) {
1601		    if (LIST_NEXT(op, nfso_list) != NULL)
1602			panic("nfsclexp");
1603		    LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) {
1604			if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner,
1605			    NFSV4CL_LOCKNAMELEN))
1606			    break;
1607		    }
1608		    if (towp != NULL) {
1609			/* Merge opens in */
1610			LIST_FOREACH(top, &towp->nfsow_open, nfso_list) {
1611			    if (top->nfso_fhlen == op->nfso_fhlen &&
1612				!NFSBCMP(top->nfso_fh, op->nfso_fh,
1613				 op->nfso_fhlen)) {
1614				top->nfso_mode |= op->nfso_mode;
1615				top->nfso_opencnt += op->nfso_opencnt;
1616				break;
1617			    }
1618			}
1619			if (top == NULL) {
1620			    /* Just add the open to the owner list */
1621			    LIST_REMOVE(op, nfso_list);
1622			    op->nfso_own = towp;
1623			    LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list);
1624			    newnfsstats.cllocalopens--;
1625			    newnfsstats.clopens++;
1626			}
1627		    } else {
1628			/* Just add the openowner to the client list */
1629			LIST_REMOVE(owp, nfsow_list);
1630			owp->nfsow_clp = clp;
1631			LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list);
1632			newnfsstats.cllocalopenowners--;
1633			newnfsstats.clopenowners++;
1634			newnfsstats.cllocalopens--;
1635			newnfsstats.clopens++;
1636		    }
1637		}
1638		owp = nowp;
1639	    }
1640	    if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) {
1641		printed = 1;
1642		printf("nfsv4 expired locks lost\n");
1643	    }
1644	    nfscl_cleandeleg(dp);
1645	    nfscl_freedeleg(&clp->nfsc_deleg, dp);
1646	    dp = ndp;
1647	}
1648	if (!TAILQ_EMPTY(&clp->nfsc_deleg))
1649	    panic("nfsclexp");
1650
1651	/*
1652	 * Now, try and reopen against the server.
1653	 */
1654	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1655		owp->nfsow_seqid = 0;
1656		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1657			ret = nfscl_expireopen(clp, op, nmp, cred, p);
1658			if (ret && !printed) {
1659				printed = 1;
1660				printf("nfsv4 expired locks lost\n");
1661			}
1662		}
1663		if (LIST_EMPTY(&owp->nfsow_open))
1664			nfscl_freeopenowner(owp, 0);
1665	}
1666}
1667
1668/*
1669 * This function must be called after the process represented by "own" has
1670 * exited. Must be called with CLSTATE lock held.
1671 */
1672static void
1673nfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own)
1674{
1675	struct nfsclowner *owp, *nowp;
1676	struct nfscllockowner *lp, *nlp;
1677	struct nfscldeleg *dp;
1678
1679	/* First, get rid of local locks on delegations. */
1680	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1681		LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) {
1682		    if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
1683			if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1684			    panic("nfscllckw");
1685			nfscl_freelockowner(lp, 1);
1686		    }
1687		}
1688	}
1689	owp = LIST_FIRST(&clp->nfsc_owner);
1690	while (owp != NULL) {
1691		nowp = LIST_NEXT(owp, nfsow_list);
1692		if (!NFSBCMP(owp->nfsow_owner, own,
1693		    NFSV4CL_LOCKNAMELEN)) {
1694			/*
1695			 * If there are children that haven't closed the
1696			 * file descriptors yet, the opens will still be
1697			 * here. For that case, let the renew thread clear
1698			 * out the OpenOwner later.
1699			 */
1700			if (LIST_EMPTY(&owp->nfsow_open))
1701				nfscl_freeopenowner(owp, 0);
1702			else
1703				owp->nfsow_defunct = 1;
1704		}
1705		owp = nowp;
1706	}
1707}
1708
1709/*
1710 * Find open/lock owners for processes that have exited.
1711 */
1712static void
1713nfscl_cleanupkext(struct nfsclclient *clp, struct nfscllockownerfhhead *lhp)
1714{
1715	struct nfsclowner *owp, *nowp;
1716	struct nfsclopen *op;
1717	struct nfscllockowner *lp, *nlp;
1718
1719	NFSPROCLISTLOCK();
1720	NFSLOCKCLSTATE();
1721	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1722		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1723			LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp) {
1724				if (LIST_EMPTY(&lp->nfsl_lock))
1725					nfscl_emptylockowner(lp, lhp);
1726			}
1727		}
1728		if (nfscl_procdoesntexist(owp->nfsow_owner))
1729			nfscl_cleanup_common(clp, owp->nfsow_owner);
1730	}
1731	NFSUNLOCKCLSTATE();
1732	NFSPROCLISTUNLOCK();
1733}
1734
1735/*
1736 * Take the empty lock owner and move it to the local lhp list if the
1737 * associated process no longer exists.
1738 */
1739static void
1740nfscl_emptylockowner(struct nfscllockowner *lp,
1741    struct nfscllockownerfhhead *lhp)
1742{
1743	struct nfscllockownerfh *lfhp, *mylfhp;
1744	struct nfscllockowner *nlp;
1745	int fnd_it;
1746
1747	/* If not a Posix lock owner, just return. */
1748	if ((lp->nfsl_lockflags & F_POSIX) == 0)
1749		return;
1750
1751	fnd_it = 0;
1752	mylfhp = NULL;
1753	/*
1754	 * First, search to see if this lock owner is already in the list.
1755	 * If it is, then the associated process no longer exists.
1756	 */
1757	SLIST_FOREACH(lfhp, lhp, nfslfh_list) {
1758		if (lfhp->nfslfh_len == lp->nfsl_open->nfso_fhlen &&
1759		    !NFSBCMP(lfhp->nfslfh_fh, lp->nfsl_open->nfso_fh,
1760		    lfhp->nfslfh_len))
1761			mylfhp = lfhp;
1762		LIST_FOREACH(nlp, &lfhp->nfslfh_lock, nfsl_list)
1763			if (!NFSBCMP(nlp->nfsl_owner, lp->nfsl_owner,
1764			    NFSV4CL_LOCKNAMELEN))
1765				fnd_it = 1;
1766	}
1767	/* If not found, check if process still exists. */
1768	if (fnd_it == 0 && nfscl_procdoesntexist(lp->nfsl_owner) == 0)
1769		return;
1770
1771	/* Move the lock owner over to the local list. */
1772	if (mylfhp == NULL) {
1773		mylfhp = malloc(sizeof(struct nfscllockownerfh), M_TEMP,
1774		    M_NOWAIT);
1775		if (mylfhp == NULL)
1776			return;
1777		mylfhp->nfslfh_len = lp->nfsl_open->nfso_fhlen;
1778		NFSBCOPY(lp->nfsl_open->nfso_fh, mylfhp->nfslfh_fh,
1779		    mylfhp->nfslfh_len);
1780		LIST_INIT(&mylfhp->nfslfh_lock);
1781		SLIST_INSERT_HEAD(lhp, mylfhp, nfslfh_list);
1782	}
1783	LIST_REMOVE(lp, nfsl_list);
1784	LIST_INSERT_HEAD(&mylfhp->nfslfh_lock, lp, nfsl_list);
1785}
1786
1787static int	fake_global;	/* Used to force visibility of MNTK_UNMOUNTF */
1788/*
1789 * Called from nfs umount to free up the clientid.
1790 */
1791APPLESTATIC void
1792nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
1793{
1794	struct nfsclclient *clp;
1795	struct ucred *cred;
1796	int igotlock;
1797
1798	/*
1799	 * For the case that matters, this is the thread that set
1800	 * MNTK_UNMOUNTF, so it will see it set. The code that follows is
1801	 * done to ensure that any thread executing nfscl_getcl() after
1802	 * this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the
1803	 * mutex for NFSLOCKCLSTATE(), so it is "m" for the following
1804	 * explanation, courtesy of Alan Cox.
1805	 * What follows is a snippet from Alan Cox's email at:
1806	 * http://docs.FreeBSD.org/cgi/
1807	 *     mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw
1808	 *
1809	 * 1. Set MNTK_UNMOUNTF
1810	 * 2. Acquire a standard FreeBSD mutex "m".
1811	 * 3. Update some data structures.
1812	 * 4. Release mutex "m".
1813	 *
1814	 * Then, other threads that acquire "m" after step 4 has occurred will
1815	 * see MNTK_UNMOUNTF as set.  But, other threads that beat thread X to
1816	 * step 2 may or may not see MNTK_UNMOUNTF as set.
1817	 */
1818	NFSLOCKCLSTATE();
1819	if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1820		fake_global++;
1821		NFSUNLOCKCLSTATE();
1822		NFSLOCKCLSTATE();
1823	}
1824
1825	clp = nmp->nm_clp;
1826	if (clp != NULL) {
1827		if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
1828			panic("nfscl umount");
1829
1830		/*
1831		 * First, handshake with the nfscl renew thread, to terminate
1832		 * it.
1833		 */
1834		clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
1835		while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
1836			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT,
1837			    "nfsclumnt", hz);
1838
1839		/*
1840		 * Now, get the exclusive lock on the client state, so
1841		 * that no uses of the state are still in progress.
1842		 */
1843		do {
1844			igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1845			    NFSCLSTATEMUTEXPTR, NULL);
1846		} while (!igotlock);
1847		NFSUNLOCKCLSTATE();
1848
1849		/*
1850		 * Free up all the state. It will expire on the server, but
1851		 * maybe we should do a SetClientId/SetClientIdConfirm so
1852		 * the server throws it away?
1853		 */
1854		LIST_REMOVE(clp, nfsc_list);
1855		nfscl_delegreturnall(clp, p);
1856		cred = newnfs_getcred();
1857		if (NFSHASNFSV4N(nmp)) {
1858			(void)nfsrpc_destroysession(nmp, clp, cred, p);
1859			(void)nfsrpc_destroyclient(nmp, clp, cred, p);
1860		} else
1861			(void)nfsrpc_setclient(nmp, clp, 0, cred, p);
1862		nfscl_cleanclient(clp);
1863		nmp->nm_clp = NULL;
1864		NFSFREECRED(cred);
1865		free(clp, M_NFSCLCLIENT);
1866	} else
1867		NFSUNLOCKCLSTATE();
1868}
1869
1870/*
1871 * This function is called when a server replies with NFSERR_STALECLIENTID
1872 * NFSERR_STALESTATEID or NFSERR_BADSESSION. It traverses the clientid lists,
1873 * doing Opens and Locks with reclaim. If these fail, it deletes the
1874 * corresponding state.
1875 */
1876static void
1877nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
1878{
1879	struct nfsclowner *owp, *nowp;
1880	struct nfsclopen *op, *nop;
1881	struct nfscllockowner *lp, *nlp;
1882	struct nfscllock *lop, *nlop;
1883	struct nfscldeleg *dp, *ndp, *tdp;
1884	struct nfsmount *nmp;
1885	struct ucred *tcred;
1886	struct nfsclopenhead extra_open;
1887	struct nfscldeleghead extra_deleg;
1888	struct nfsreq *rep;
1889	u_int64_t len;
1890	u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
1891	int i, igotlock = 0, error, trycnt, firstlock;
1892	struct nfscllayout *lyp, *nlyp;
1893
1894	/*
1895	 * First, lock the client structure, so everyone else will
1896	 * block when trying to use state.
1897	 */
1898	NFSLOCKCLSTATE();
1899	clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
1900	do {
1901		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1902		    NFSCLSTATEMUTEXPTR, NULL);
1903	} while (!igotlock);
1904	NFSUNLOCKCLSTATE();
1905
1906	nmp = clp->nfsc_nmp;
1907	if (nmp == NULL)
1908		panic("nfscl recover");
1909
1910	/*
1911	 * For now, just get rid of all layouts. There may be a need
1912	 * to do LayoutCommit Ops with reclaim == true later.
1913	 */
1914	TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp)
1915		nfscl_freelayout(lyp);
1916	TAILQ_INIT(&clp->nfsc_layout);
1917	for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++)
1918		LIST_INIT(&clp->nfsc_layouthash[i]);
1919
1920	trycnt = 5;
1921	do {
1922		error = nfsrpc_setclient(nmp, clp, 1, cred, p);
1923	} while ((error == NFSERR_STALECLIENTID ||
1924	     error == NFSERR_BADSESSION ||
1925	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
1926	if (error) {
1927		nfscl_cleanclient(clp);
1928		NFSLOCKCLSTATE();
1929		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
1930		    NFSCLFLAGS_RECOVER | NFSCLFLAGS_RECVRINPROG);
1931		wakeup(&clp->nfsc_flags);
1932		nfsv4_unlock(&clp->nfsc_lock, 0);
1933		NFSUNLOCKCLSTATE();
1934		return;
1935	}
1936	clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
1937	clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
1938
1939	/*
1940	 * Mark requests already queued on the server, so that they don't
1941	 * initiate another recovery cycle. Any requests already in the
1942	 * queue that handle state information will have the old stale
1943	 * clientid/stateid and will get a NFSERR_STALESTATEID,
1944	 * NFSERR_STALECLIENTID or NFSERR_BADSESSION reply from the server.
1945	 * This will be translated to NFSERR_STALEDONTRECOVER when
1946	 * R_DONTRECOVER is set.
1947	 */
1948	NFSLOCKREQ();
1949	TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) {
1950		if (rep->r_nmp == nmp)
1951			rep->r_flags |= R_DONTRECOVER;
1952	}
1953	NFSUNLOCKREQ();
1954
1955	/*
1956	 * Now, mark all delegations "need reclaim".
1957	 */
1958	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
1959		dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM;
1960
1961	TAILQ_INIT(&extra_deleg);
1962	LIST_INIT(&extra_open);
1963	/*
1964	 * Now traverse the state lists, doing Open and Lock Reclaims.
1965	 */
1966	tcred = newnfs_getcred();
1967	owp = LIST_FIRST(&clp->nfsc_owner);
1968	while (owp != NULL) {
1969	    nowp = LIST_NEXT(owp, nfsow_list);
1970	    owp->nfsow_seqid = 0;
1971	    op = LIST_FIRST(&owp->nfsow_open);
1972	    while (op != NULL) {
1973		nop = LIST_NEXT(op, nfso_list);
1974		if (error != NFSERR_NOGRACE) {
1975		    /* Search for a delegation to reclaim with the open */
1976		    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1977			if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1978			    continue;
1979			if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
1980			    mode = NFSV4OPEN_ACCESSWRITE;
1981			    delegtype = NFSV4OPEN_DELEGATEWRITE;
1982			} else {
1983			    mode = NFSV4OPEN_ACCESSREAD;
1984			    delegtype = NFSV4OPEN_DELEGATEREAD;
1985			}
1986			if ((op->nfso_mode & mode) == mode &&
1987			    op->nfso_fhlen == dp->nfsdl_fhlen &&
1988			    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen))
1989			    break;
1990		    }
1991		    ndp = dp;
1992		    if (dp == NULL)
1993			delegtype = NFSV4OPEN_DELEGATENONE;
1994		    newnfs_copycred(&op->nfso_cred, tcred);
1995		    error = nfscl_tryopen(nmp, NULL, op->nfso_fh,
1996			op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen,
1997			op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
1998			tcred, p);
1999		    if (!error) {
2000			/* Handle any replied delegation */
2001			if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
2002			    || NFSMNT_RDONLY(nmp->nm_mountp))) {
2003			    if ((ndp->nfsdl_flags & NFSCLDL_WRITE))
2004				mode = NFSV4OPEN_ACCESSWRITE;
2005			    else
2006				mode = NFSV4OPEN_ACCESSREAD;
2007			    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2008				if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
2009				    continue;
2010				if ((op->nfso_mode & mode) == mode &&
2011				    op->nfso_fhlen == dp->nfsdl_fhlen &&
2012				    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh,
2013				    op->nfso_fhlen)) {
2014				    dp->nfsdl_stateid = ndp->nfsdl_stateid;
2015				    dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit;
2016				    dp->nfsdl_ace = ndp->nfsdl_ace;
2017				    dp->nfsdl_change = ndp->nfsdl_change;
2018				    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2019				    if ((ndp->nfsdl_flags & NFSCLDL_RECALL))
2020					dp->nfsdl_flags |= NFSCLDL_RECALL;
2021				    FREE((caddr_t)ndp, M_NFSCLDELEG);
2022				    ndp = NULL;
2023				    break;
2024				}
2025			    }
2026			}
2027			if (ndp != NULL)
2028			    TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list);
2029
2030			/* and reclaim all byte range locks */
2031			lp = LIST_FIRST(&op->nfso_lock);
2032			while (lp != NULL) {
2033			    nlp = LIST_NEXT(lp, nfsl_list);
2034			    lp->nfsl_seqid = 0;
2035			    firstlock = 1;
2036			    lop = LIST_FIRST(&lp->nfsl_lock);
2037			    while (lop != NULL) {
2038				nlop = LIST_NEXT(lop, nfslo_list);
2039				if (lop->nfslo_end == NFS64BITSSET)
2040				    len = NFS64BITSSET;
2041				else
2042				    len = lop->nfslo_end - lop->nfslo_first;
2043				if (error != NFSERR_NOGRACE)
2044				    error = nfscl_trylock(nmp, NULL,
2045					op->nfso_fh, op->nfso_fhlen, lp,
2046					firstlock, 1, lop->nfslo_first, len,
2047					lop->nfslo_type, tcred, p);
2048				if (error != 0)
2049				    nfscl_freelock(lop, 0);
2050				else
2051				    firstlock = 0;
2052				lop = nlop;
2053			    }
2054			    /* If no locks, but a lockowner, just delete it. */
2055			    if (LIST_EMPTY(&lp->nfsl_lock))
2056				nfscl_freelockowner(lp, 0);
2057			    lp = nlp;
2058			}
2059		    } else {
2060			nfscl_freeopen(op, 0);
2061		    }
2062		}
2063		op = nop;
2064	    }
2065	    owp = nowp;
2066	}
2067
2068	/*
2069	 * Now, try and get any delegations not yet reclaimed by cobbling
2070	 * to-gether an appropriate open.
2071	 */
2072	nowp = NULL;
2073	dp = TAILQ_FIRST(&clp->nfsc_deleg);
2074	while (dp != NULL) {
2075	    ndp = TAILQ_NEXT(dp, nfsdl_list);
2076	    if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) {
2077		if (nowp == NULL) {
2078		    MALLOC(nowp, struct nfsclowner *,
2079			sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK);
2080		    /*
2081		     * Name must be as long an largest possible
2082		     * NFSV4CL_LOCKNAMELEN. 12 for now.
2083		     */
2084		    NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner,
2085			NFSV4CL_LOCKNAMELEN);
2086		    LIST_INIT(&nowp->nfsow_open);
2087		    nowp->nfsow_clp = clp;
2088		    nowp->nfsow_seqid = 0;
2089		    nowp->nfsow_defunct = 0;
2090		    nfscl_lockinit(&nowp->nfsow_rwlock);
2091		}
2092		nop = NULL;
2093		if (error != NFSERR_NOGRACE) {
2094		    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
2095			dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
2096		    nop->nfso_own = nowp;
2097		    if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
2098			nop->nfso_mode = NFSV4OPEN_ACCESSWRITE;
2099			delegtype = NFSV4OPEN_DELEGATEWRITE;
2100		    } else {
2101			nop->nfso_mode = NFSV4OPEN_ACCESSREAD;
2102			delegtype = NFSV4OPEN_DELEGATEREAD;
2103		    }
2104		    nop->nfso_opencnt = 0;
2105		    nop->nfso_posixlock = 1;
2106		    nop->nfso_fhlen = dp->nfsdl_fhlen;
2107		    NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen);
2108		    LIST_INIT(&nop->nfso_lock);
2109		    nop->nfso_stateid.seqid = 0;
2110		    nop->nfso_stateid.other[0] = 0;
2111		    nop->nfso_stateid.other[1] = 0;
2112		    nop->nfso_stateid.other[2] = 0;
2113		    newnfs_copycred(&dp->nfsdl_cred, tcred);
2114		    newnfs_copyincred(tcred, &nop->nfso_cred);
2115		    tdp = NULL;
2116		    error = nfscl_tryopen(nmp, NULL, nop->nfso_fh,
2117			nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen,
2118			nop->nfso_mode, nop, NULL, 0, &tdp, 1,
2119			delegtype, tcred, p);
2120		    if (tdp != NULL) {
2121			if ((tdp->nfsdl_flags & NFSCLDL_WRITE))
2122			    mode = NFSV4OPEN_ACCESSWRITE;
2123			else
2124			    mode = NFSV4OPEN_ACCESSREAD;
2125			if ((nop->nfso_mode & mode) == mode &&
2126			    nop->nfso_fhlen == tdp->nfsdl_fhlen &&
2127			    !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh,
2128			    nop->nfso_fhlen)) {
2129			    dp->nfsdl_stateid = tdp->nfsdl_stateid;
2130			    dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit;
2131			    dp->nfsdl_ace = tdp->nfsdl_ace;
2132			    dp->nfsdl_change = tdp->nfsdl_change;
2133			    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2134			    if ((tdp->nfsdl_flags & NFSCLDL_RECALL))
2135				dp->nfsdl_flags |= NFSCLDL_RECALL;
2136			    FREE((caddr_t)tdp, M_NFSCLDELEG);
2137			} else {
2138			    TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list);
2139			}
2140		    }
2141		}
2142		if (error) {
2143		    if (nop != NULL)
2144			FREE((caddr_t)nop, M_NFSCLOPEN);
2145		    /*
2146		     * Couldn't reclaim it, so throw the state
2147		     * away. Ouch!!
2148		     */
2149		    nfscl_cleandeleg(dp);
2150		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
2151		} else {
2152		    LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
2153		}
2154	    }
2155	    dp = ndp;
2156	}
2157
2158	/*
2159	 * Now, get rid of extra Opens and Delegations.
2160	 */
2161	LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) {
2162		do {
2163			newnfs_copycred(&op->nfso_cred, tcred);
2164			error = nfscl_tryclose(op, tcred, nmp, p);
2165			if (error == NFSERR_GRACE)
2166				(void) nfs_catnap(PZERO, error, "nfsexcls");
2167		} while (error == NFSERR_GRACE);
2168		LIST_REMOVE(op, nfso_list);
2169		FREE((caddr_t)op, M_NFSCLOPEN);
2170	}
2171	if (nowp != NULL)
2172		FREE((caddr_t)nowp, M_NFSCLOWNER);
2173
2174	TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) {
2175		do {
2176			newnfs_copycred(&dp->nfsdl_cred, tcred);
2177			error = nfscl_trydelegreturn(dp, tcred, nmp, p);
2178			if (error == NFSERR_GRACE)
2179				(void) nfs_catnap(PZERO, error, "nfsexdlg");
2180		} while (error == NFSERR_GRACE);
2181		TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list);
2182		FREE((caddr_t)dp, M_NFSCLDELEG);
2183	}
2184
2185	/* For NFSv4.1 or later, do a RECLAIM_COMPLETE. */
2186	if (NFSHASNFSV4N(nmp))
2187		(void)nfsrpc_reclaimcomplete(nmp, cred, p);
2188
2189	NFSLOCKCLSTATE();
2190	clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG;
2191	wakeup(&clp->nfsc_flags);
2192	nfsv4_unlock(&clp->nfsc_lock, 0);
2193	NFSUNLOCKCLSTATE();
2194	NFSFREECRED(tcred);
2195}
2196
2197/*
2198 * This function is called when a server replies with NFSERR_EXPIRED.
2199 * It deletes all state for the client and does a fresh SetClientId/confirm.
2200 * XXX Someday it should post a signal to the process(es) that hold the
2201 * state, so they know that lock state has been lost.
2202 */
2203APPLESTATIC int
2204nfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
2205{
2206	struct nfsmount *nmp;
2207	struct ucred *cred;
2208	int igotlock = 0, error, trycnt;
2209
2210	/*
2211	 * If the clientid has gone away or a new SetClientid has already
2212	 * been done, just return ok.
2213	 */
2214	if (clp == NULL || clidrev != clp->nfsc_clientidrev)
2215		return (0);
2216
2217	/*
2218	 * First, lock the client structure, so everyone else will
2219	 * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so
2220	 * that only one thread does the work.
2221	 */
2222	NFSLOCKCLSTATE();
2223	clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
2224	do {
2225		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2226		    NFSCLSTATEMUTEXPTR, NULL);
2227	} while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
2228	if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
2229		if (igotlock)
2230			nfsv4_unlock(&clp->nfsc_lock, 0);
2231		NFSUNLOCKCLSTATE();
2232		return (0);
2233	}
2234	clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
2235	NFSUNLOCKCLSTATE();
2236
2237	nmp = clp->nfsc_nmp;
2238	if (nmp == NULL)
2239		panic("nfscl expired");
2240	cred = newnfs_getcred();
2241	trycnt = 5;
2242	do {
2243		error = nfsrpc_setclient(nmp, clp, 0, cred, p);
2244	} while ((error == NFSERR_STALECLIENTID ||
2245	     error == NFSERR_BADSESSION ||
2246	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
2247	if (error) {
2248		/*
2249		 * Clear out any state.
2250		 */
2251		nfscl_cleanclient(clp);
2252		NFSLOCKCLSTATE();
2253		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
2254		    NFSCLFLAGS_RECOVER);
2255	} else {
2256		/*
2257		 * Expire the state for the client.
2258		 */
2259		nfscl_expireclient(clp, nmp, cred, p);
2260		NFSLOCKCLSTATE();
2261		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
2262		clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2263	}
2264	clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG);
2265	wakeup(&clp->nfsc_flags);
2266	nfsv4_unlock(&clp->nfsc_lock, 0);
2267	NFSUNLOCKCLSTATE();
2268	NFSFREECRED(cred);
2269	return (error);
2270}
2271
2272/*
2273 * This function inserts a lock in the list after insert_lop.
2274 */
2275static void
2276nfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop,
2277    struct nfscllock *insert_lop, int local)
2278{
2279
2280	if ((struct nfscllockowner *)insert_lop == lp)
2281		LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list);
2282	else
2283		LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list);
2284	if (local)
2285		newnfsstats.cllocallocks++;
2286	else
2287		newnfsstats.cllocks++;
2288}
2289
2290/*
2291 * This function updates the locking for a lock owner and given file. It
2292 * maintains a list of lock ranges ordered on increasing file offset that
2293 * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style).
2294 * It always adds new_lop to the list and sometimes uses the one pointed
2295 * at by other_lopp.
2296 * Returns 1 if the locks were modified, 0 otherwise.
2297 */
2298static int
2299nfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp,
2300    struct nfscllock **other_lopp, int local)
2301{
2302	struct nfscllock *new_lop = *new_lopp;
2303	struct nfscllock *lop, *tlop, *ilop;
2304	struct nfscllock *other_lop;
2305	int unlock = 0, modified = 0;
2306	u_int64_t tmp;
2307
2308	/*
2309	 * Work down the list until the lock is merged.
2310	 */
2311	if (new_lop->nfslo_type == F_UNLCK)
2312		unlock = 1;
2313	ilop = (struct nfscllock *)lp;
2314	lop = LIST_FIRST(&lp->nfsl_lock);
2315	while (lop != NULL) {
2316	    /*
2317	     * Only check locks for this file that aren't before the start of
2318	     * new lock's range.
2319	     */
2320	    if (lop->nfslo_end >= new_lop->nfslo_first) {
2321		if (new_lop->nfslo_end < lop->nfslo_first) {
2322		    /*
2323		     * If the new lock ends before the start of the
2324		     * current lock's range, no merge, just insert
2325		     * the new lock.
2326		     */
2327		    break;
2328		}
2329		if (new_lop->nfslo_type == lop->nfslo_type ||
2330		    (new_lop->nfslo_first <= lop->nfslo_first &&
2331		     new_lop->nfslo_end >= lop->nfslo_end)) {
2332		    /*
2333		     * This lock can be absorbed by the new lock/unlock.
2334		     * This happens when it covers the entire range
2335		     * of the old lock or is contiguous
2336		     * with the old lock and is of the same type or an
2337		     * unlock.
2338		     */
2339		    if (new_lop->nfslo_type != lop->nfslo_type ||
2340			new_lop->nfslo_first != lop->nfslo_first ||
2341			new_lop->nfslo_end != lop->nfslo_end)
2342			modified = 1;
2343		    if (lop->nfslo_first < new_lop->nfslo_first)
2344			new_lop->nfslo_first = lop->nfslo_first;
2345		    if (lop->nfslo_end > new_lop->nfslo_end)
2346			new_lop->nfslo_end = lop->nfslo_end;
2347		    tlop = lop;
2348		    lop = LIST_NEXT(lop, nfslo_list);
2349		    nfscl_freelock(tlop, local);
2350		    continue;
2351		}
2352
2353		/*
2354		 * All these cases are for contiguous locks that are not the
2355		 * same type, so they can't be merged.
2356		 */
2357		if (new_lop->nfslo_first <= lop->nfslo_first) {
2358		    /*
2359		     * This case is where the new lock overlaps with the
2360		     * first part of the old lock. Move the start of the
2361		     * old lock to just past the end of the new lock. The
2362		     * new lock will be inserted in front of the old, since
2363		     * ilop hasn't been updated. (We are done now.)
2364		     */
2365		    if (lop->nfslo_first != new_lop->nfslo_end) {
2366			lop->nfslo_first = new_lop->nfslo_end;
2367			modified = 1;
2368		    }
2369		    break;
2370		}
2371		if (new_lop->nfslo_end >= lop->nfslo_end) {
2372		    /*
2373		     * This case is where the new lock overlaps with the
2374		     * end of the old lock's range. Move the old lock's
2375		     * end to just before the new lock's first and insert
2376		     * the new lock after the old lock.
2377		     * Might not be done yet, since the new lock could
2378		     * overlap further locks with higher ranges.
2379		     */
2380		    if (lop->nfslo_end != new_lop->nfslo_first) {
2381			lop->nfslo_end = new_lop->nfslo_first;
2382			modified = 1;
2383		    }
2384		    ilop = lop;
2385		    lop = LIST_NEXT(lop, nfslo_list);
2386		    continue;
2387		}
2388		/*
2389		 * The final case is where the new lock's range is in the
2390		 * middle of the current lock's and splits the current lock
2391		 * up. Use *other_lopp to handle the second part of the
2392		 * split old lock range. (We are done now.)
2393		 * For unlock, we use new_lop as other_lop and tmp, since
2394		 * other_lop and new_lop are the same for this case.
2395		 * We noted the unlock case above, so we don't need
2396		 * new_lop->nfslo_type any longer.
2397		 */
2398		tmp = new_lop->nfslo_first;
2399		if (unlock) {
2400		    other_lop = new_lop;
2401		    *new_lopp = NULL;
2402		} else {
2403		    other_lop = *other_lopp;
2404		    *other_lopp = NULL;
2405		}
2406		other_lop->nfslo_first = new_lop->nfslo_end;
2407		other_lop->nfslo_end = lop->nfslo_end;
2408		other_lop->nfslo_type = lop->nfslo_type;
2409		lop->nfslo_end = tmp;
2410		nfscl_insertlock(lp, other_lop, lop, local);
2411		ilop = lop;
2412		modified = 1;
2413		break;
2414	    }
2415	    ilop = lop;
2416	    lop = LIST_NEXT(lop, nfslo_list);
2417	    if (lop == NULL)
2418		break;
2419	}
2420
2421	/*
2422	 * Insert the new lock in the list at the appropriate place.
2423	 */
2424	if (!unlock) {
2425		nfscl_insertlock(lp, new_lop, ilop, local);
2426		*new_lopp = NULL;
2427		modified = 1;
2428	}
2429	return (modified);
2430}
2431
2432/*
2433 * This function must be run as a kernel thread.
2434 * It does Renew Ops and recovery, when required.
2435 */
2436APPLESTATIC void
2437nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
2438{
2439	struct nfsclowner *owp, *nowp;
2440	struct nfsclopen *op;
2441	struct nfscllockowner *lp, *nlp;
2442	struct nfscldeleghead dh;
2443	struct nfscldeleg *dp, *ndp;
2444	struct ucred *cred;
2445	u_int32_t clidrev;
2446	int error, cbpathdown, islept, igotlock, ret, clearok;
2447	uint32_t recover_done_time = 0;
2448	time_t mytime;
2449	static time_t prevsec = 0;
2450	struct nfscllockownerfh *lfhp, *nlfhp;
2451	struct nfscllockownerfhhead lfh;
2452	struct nfscllayout *lyp, *nlyp;
2453	struct nfscldevinfo *dip, *ndip;
2454	struct nfscllayouthead rlh;
2455	struct nfsclrecalllayout *recallp;
2456	struct nfsclds *dsp;
2457
2458	cred = newnfs_getcred();
2459	NFSLOCKCLSTATE();
2460	clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD;
2461	NFSUNLOCKCLSTATE();
2462	for(;;) {
2463		newnfs_setroot(cred);
2464		cbpathdown = 0;
2465		if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) {
2466			/*
2467			 * Only allow one recover within 1/2 of the lease
2468			 * duration (nfsc_renew).
2469			 */
2470			if (recover_done_time < NFSD_MONOSEC) {
2471				recover_done_time = NFSD_MONOSEC +
2472				    clp->nfsc_renew;
2473				NFSCL_DEBUG(1, "Doing recovery..\n");
2474				nfscl_recover(clp, cred, p);
2475			} else {
2476				NFSCL_DEBUG(1, "Clear Recovery dt=%u ms=%jd\n",
2477				    recover_done_time, (intmax_t)NFSD_MONOSEC);
2478				NFSLOCKCLSTATE();
2479				clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2480				NFSUNLOCKCLSTATE();
2481			}
2482		}
2483		if (clp->nfsc_expire <= NFSD_MONOSEC &&
2484		    (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
2485			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
2486			clidrev = clp->nfsc_clientidrev;
2487			error = nfsrpc_renew(clp, NULL, cred, p);
2488			if (error == NFSERR_CBPATHDOWN)
2489			    cbpathdown = 1;
2490			else if (error == NFSERR_STALECLIENTID ||
2491			    error == NFSERR_BADSESSION) {
2492			    NFSLOCKCLSTATE();
2493			    clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2494			    NFSUNLOCKCLSTATE();
2495			} else if (error == NFSERR_EXPIRED)
2496			    (void) nfscl_hasexpired(clp, clidrev, p);
2497		}
2498
2499checkdsrenew:
2500		if (NFSHASNFSV4N(clp->nfsc_nmp)) {
2501			/* Do renews for any DS sessions. */
2502			NFSLOCKMNT(clp->nfsc_nmp);
2503			/* Skip first entry, since the MDS is handled above. */
2504			dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess);
2505			if (dsp != NULL)
2506				dsp = TAILQ_NEXT(dsp, nfsclds_list);
2507			while (dsp != NULL) {
2508				if (dsp->nfsclds_expire <= NFSD_MONOSEC &&
2509				    dsp->nfsclds_sess.nfsess_defunct == 0) {
2510					dsp->nfsclds_expire = NFSD_MONOSEC +
2511					    clp->nfsc_renew;
2512					NFSUNLOCKMNT(clp->nfsc_nmp);
2513					(void)nfsrpc_renew(clp, dsp, cred, p);
2514					goto checkdsrenew;
2515				}
2516				dsp = TAILQ_NEXT(dsp, nfsclds_list);
2517			}
2518			NFSUNLOCKMNT(clp->nfsc_nmp);
2519		}
2520
2521		TAILQ_INIT(&dh);
2522		NFSLOCKCLSTATE();
2523		if (cbpathdown)
2524			/* It's a Total Recall! */
2525			nfscl_totalrecall(clp);
2526
2527		/*
2528		 * Now, handle defunct owners.
2529		 */
2530		LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
2531			if (LIST_EMPTY(&owp->nfsow_open)) {
2532				if (owp->nfsow_defunct != 0)
2533					nfscl_freeopenowner(owp, 0);
2534			}
2535		}
2536
2537		/*
2538		 * Do the recall on any delegations. To avoid trouble, always
2539		 * come back up here after having slept.
2540		 */
2541		igotlock = 0;
2542tryagain:
2543		dp = TAILQ_FIRST(&clp->nfsc_deleg);
2544		while (dp != NULL) {
2545			ndp = TAILQ_NEXT(dp, nfsdl_list);
2546			if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
2547				/*
2548				 * Wait for outstanding I/O ops to be done.
2549				 */
2550				if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
2551				    if (igotlock) {
2552					nfsv4_unlock(&clp->nfsc_lock, 0);
2553					igotlock = 0;
2554				    }
2555				    dp->nfsdl_rwlock.nfslock_lock |=
2556					NFSV4LOCK_WANTED;
2557				    (void) nfsmsleep(&dp->nfsdl_rwlock,
2558					NFSCLSTATEMUTEXPTR, PZERO, "nfscld",
2559					NULL);
2560				    goto tryagain;
2561				}
2562				while (!igotlock) {
2563				    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
2564					&islept, NFSCLSTATEMUTEXPTR, NULL);
2565				    if (islept)
2566					goto tryagain;
2567				}
2568				NFSUNLOCKCLSTATE();
2569				newnfs_copycred(&dp->nfsdl_cred, cred);
2570				ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
2571				    NULL, cred, p, 1);
2572				if (!ret) {
2573				    nfscl_cleandeleg(dp);
2574				    TAILQ_REMOVE(&clp->nfsc_deleg, dp,
2575					nfsdl_list);
2576				    LIST_REMOVE(dp, nfsdl_hash);
2577				    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2578				    nfscl_delegcnt--;
2579				    newnfsstats.cldelegates--;
2580				}
2581				NFSLOCKCLSTATE();
2582			}
2583			dp = ndp;
2584		}
2585
2586		/*
2587		 * Clear out old delegations, if we are above the high water
2588		 * mark. Only clear out ones with no state related to them.
2589		 * The tailq list is in LRU order.
2590		 */
2591		dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
2592		while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) {
2593		    ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
2594		    if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
2595			dp->nfsdl_rwlock.nfslock_lock == 0 &&
2596			dp->nfsdl_timestamp < NFSD_MONOSEC &&
2597			(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
2598			  NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) {
2599			clearok = 1;
2600			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2601			    op = LIST_FIRST(&owp->nfsow_open);
2602			    if (op != NULL) {
2603				clearok = 0;
2604				break;
2605			    }
2606			}
2607			if (clearok) {
2608			    LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
2609				if (!LIST_EMPTY(&lp->nfsl_lock)) {
2610				    clearok = 0;
2611				    break;
2612				}
2613			    }
2614			}
2615			if (clearok) {
2616			    TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
2617			    LIST_REMOVE(dp, nfsdl_hash);
2618			    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2619			    nfscl_delegcnt--;
2620			    newnfsstats.cldelegates--;
2621			}
2622		    }
2623		    dp = ndp;
2624		}
2625		if (igotlock)
2626			nfsv4_unlock(&clp->nfsc_lock, 0);
2627
2628		/*
2629		 * Do the recall on any layouts. To avoid trouble, always
2630		 * come back up here after having slept.
2631		 */
2632		TAILQ_INIT(&rlh);
2633tryagain2:
2634		TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) {
2635			if ((lyp->nfsly_flags & NFSLY_RECALL) != 0) {
2636				/*
2637				 * Wait for outstanding I/O ops to be done.
2638				 */
2639				if (lyp->nfsly_lock.nfslock_usecnt > 0 ||
2640				    (lyp->nfsly_lock.nfslock_lock &
2641				     NFSV4LOCK_LOCK) != 0) {
2642					lyp->nfsly_lock.nfslock_lock |=
2643					    NFSV4LOCK_WANTED;
2644					(void)nfsmsleep(&lyp->nfsly_lock,
2645					    NFSCLSTATEMUTEXPTR, PZERO, "nfslyp",
2646					    NULL);
2647					goto tryagain2;
2648				}
2649				/* Move the layout to the recall list. */
2650				TAILQ_REMOVE(&clp->nfsc_layout, lyp,
2651				    nfsly_list);
2652				LIST_REMOVE(lyp, nfsly_hash);
2653				TAILQ_INSERT_HEAD(&rlh, lyp, nfsly_list);
2654
2655				/* Handle any layout commits. */
2656				if (!NFSHASNOLAYOUTCOMMIT(clp->nfsc_nmp) &&
2657				    (lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
2658					lyp->nfsly_flags &= ~NFSLY_WRITTEN;
2659					NFSUNLOCKCLSTATE();
2660					NFSCL_DEBUG(3, "do layoutcommit\n");
2661					nfscl_dolayoutcommit(clp->nfsc_nmp, lyp,
2662					    cred, p);
2663					NFSLOCKCLSTATE();
2664					goto tryagain2;
2665				}
2666			}
2667		}
2668
2669		/* Now, look for stale layouts. */
2670		lyp = TAILQ_LAST(&clp->nfsc_layout, nfscllayouthead);
2671		while (lyp != NULL) {
2672			nlyp = TAILQ_PREV(lyp, nfscllayouthead, nfsly_list);
2673			if (lyp->nfsly_timestamp < NFSD_MONOSEC &&
2674			    (lyp->nfsly_flags & NFSLY_RECALL) == 0 &&
2675			    lyp->nfsly_lock.nfslock_usecnt == 0 &&
2676			    lyp->nfsly_lock.nfslock_lock == 0) {
2677				NFSCL_DEBUG(4, "ret stale lay=%d\n",
2678				    nfscl_layoutcnt);
2679				recallp = malloc(sizeof(*recallp),
2680				    M_NFSLAYRECALL, M_NOWAIT);
2681				if (recallp == NULL)
2682					break;
2683				(void)nfscl_layoutrecall(NFSLAYOUTRETURN_FILE,
2684				    lyp, NFSLAYOUTIOMODE_ANY, 0, UINT64_MAX,
2685				    lyp->nfsly_stateid.seqid, recallp);
2686			}
2687			lyp = nlyp;
2688		}
2689
2690		/*
2691		 * Free up any unreferenced device info structures.
2692		 */
2693		LIST_FOREACH_SAFE(dip, &clp->nfsc_devinfo, nfsdi_list, ndip) {
2694			if (dip->nfsdi_layoutrefs == 0 &&
2695			    dip->nfsdi_refcnt == 0) {
2696				NFSCL_DEBUG(4, "freeing devinfo\n");
2697				LIST_REMOVE(dip, nfsdi_list);
2698				nfscl_freedevinfo(dip);
2699			}
2700		}
2701		NFSUNLOCKCLSTATE();
2702
2703		/* Do layout return(s), as required. */
2704		TAILQ_FOREACH_SAFE(lyp, &rlh, nfsly_list, nlyp) {
2705			TAILQ_REMOVE(&rlh, lyp, nfsly_list);
2706			NFSCL_DEBUG(4, "ret layout\n");
2707			nfscl_layoutreturn(clp->nfsc_nmp, lyp, cred, p);
2708			nfscl_freelayout(lyp);
2709		}
2710
2711		/*
2712		 * Delegreturn any delegations cleaned out or recalled.
2713		 */
2714		TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
2715			newnfs_copycred(&dp->nfsdl_cred, cred);
2716			(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2717			TAILQ_REMOVE(&dh, dp, nfsdl_list);
2718			FREE((caddr_t)dp, M_NFSCLDELEG);
2719		}
2720
2721		SLIST_INIT(&lfh);
2722		/*
2723		 * Call nfscl_cleanupkext() once per second to check for
2724		 * open/lock owners where the process has exited.
2725		 */
2726		mytime = NFSD_MONOSEC;
2727		if (prevsec != mytime) {
2728			prevsec = mytime;
2729			nfscl_cleanupkext(clp, &lfh);
2730		}
2731
2732		/*
2733		 * Do a ReleaseLockOwner for all lock owners where the
2734		 * associated process no longer exists, as found by
2735		 * nfscl_cleanupkext().
2736		 */
2737		newnfs_setroot(cred);
2738		SLIST_FOREACH_SAFE(lfhp, &lfh, nfslfh_list, nlfhp) {
2739			LIST_FOREACH_SAFE(lp, &lfhp->nfslfh_lock, nfsl_list,
2740			    nlp) {
2741				(void)nfsrpc_rellockown(clp->nfsc_nmp, lp,
2742				    lfhp->nfslfh_fh, lfhp->nfslfh_len, cred,
2743				    p);
2744				nfscl_freelockowner(lp, 0);
2745			}
2746			free(lfhp, M_TEMP);
2747		}
2748		SLIST_INIT(&lfh);
2749
2750		NFSLOCKCLSTATE();
2751		if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
2752			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl",
2753			    hz);
2754		if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
2755			clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
2756			NFSUNLOCKCLSTATE();
2757			NFSFREECRED(cred);
2758			wakeup((caddr_t)clp);
2759			return;
2760		}
2761		NFSUNLOCKCLSTATE();
2762	}
2763}
2764
2765/*
2766 * Initiate state recovery. Called when NFSERR_STALECLIENTID,
2767 * NFSERR_STALESTATEID or NFSERR_BADSESSION is received.
2768 */
2769APPLESTATIC void
2770nfscl_initiate_recovery(struct nfsclclient *clp)
2771{
2772
2773	if (clp == NULL)
2774		return;
2775	NFSLOCKCLSTATE();
2776	clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2777	NFSUNLOCKCLSTATE();
2778	wakeup((caddr_t)clp);
2779}
2780
2781/*
2782 * Dump out the state stuff for debugging.
2783 */
2784APPLESTATIC void
2785nfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
2786    int lockowner, int locks)
2787{
2788	struct nfsclclient *clp;
2789	struct nfsclowner *owp;
2790	struct nfsclopen *op;
2791	struct nfscllockowner *lp;
2792	struct nfscllock *lop;
2793	struct nfscldeleg *dp;
2794
2795	clp = nmp->nm_clp;
2796	if (clp == NULL) {
2797		printf("nfscl dumpstate NULL clp\n");
2798		return;
2799	}
2800	NFSLOCKCLSTATE();
2801	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2802	  LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2803	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2804		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2805		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2806		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2807		    owp->nfsow_seqid);
2808	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2809		if (opens)
2810		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2811			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2812			op->nfso_stateid.other[2], op->nfso_opencnt,
2813			op->nfso_fh[12]);
2814		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2815		    if (lockowner)
2816			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2817			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2818			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2819			    lp->nfsl_seqid,
2820			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2821			    lp->nfsl_stateid.other[2]);
2822		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2823			if (locks)
2824#ifdef __FreeBSD__
2825			    printf("lck typ=%d fst=%ju end=%ju\n",
2826				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2827				(intmax_t)lop->nfslo_end);
2828#else
2829			    printf("lck typ=%d fst=%qd end=%qd\n",
2830				lop->nfslo_type, lop->nfslo_first,
2831				lop->nfslo_end);
2832#endif
2833		    }
2834		}
2835	    }
2836	  }
2837	}
2838	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2839	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2840		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2841		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2842		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2843		    owp->nfsow_seqid);
2844	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2845		if (opens)
2846		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2847			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2848			op->nfso_stateid.other[2], op->nfso_opencnt,
2849			op->nfso_fh[12]);
2850		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2851		    if (lockowner)
2852			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2853			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2854			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2855			    lp->nfsl_seqid,
2856			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2857			    lp->nfsl_stateid.other[2]);
2858		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2859			if (locks)
2860#ifdef __FreeBSD__
2861			    printf("lck typ=%d fst=%ju end=%ju\n",
2862				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2863				(intmax_t)lop->nfslo_end);
2864#else
2865			    printf("lck typ=%d fst=%qd end=%qd\n",
2866				lop->nfslo_type, lop->nfslo_first,
2867				lop->nfslo_end);
2868#endif
2869		    }
2870		}
2871	    }
2872	}
2873	NFSUNLOCKCLSTATE();
2874}
2875
2876/*
2877 * Check for duplicate open owners and opens.
2878 * (Only used as a diagnostic aid.)
2879 */
2880APPLESTATIC void
2881nfscl_dupopen(vnode_t vp, int dupopens)
2882{
2883	struct nfsclclient *clp;
2884	struct nfsclowner *owp, *owp2;
2885	struct nfsclopen *op, *op2;
2886	struct nfsfh *nfhp;
2887
2888	clp = VFSTONFS(vnode_mount(vp))->nm_clp;
2889	if (clp == NULL) {
2890		printf("nfscl dupopen NULL clp\n");
2891		return;
2892	}
2893	nfhp = VTONFS(vp)->n_fhp;
2894	NFSLOCKCLSTATE();
2895
2896	/*
2897	 * First, search for duplicate owners.
2898	 * These should never happen!
2899	 */
2900	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2901	    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2902		if (owp != owp2 &&
2903		    !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
2904		    NFSV4CL_LOCKNAMELEN)) {
2905			NFSUNLOCKCLSTATE();
2906			printf("DUP OWNER\n");
2907			nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0);
2908			return;
2909		}
2910	    }
2911	}
2912
2913	/*
2914	 * Now, search for duplicate stateids.
2915	 * These shouldn't happen, either.
2916	 */
2917	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2918	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2919		LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2920		    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2921			if (op != op2 &&
2922			    (op->nfso_stateid.other[0] != 0 ||
2923			     op->nfso_stateid.other[1] != 0 ||
2924			     op->nfso_stateid.other[2] != 0) &&
2925			    op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
2926			    op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
2927			    op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
2928			    NFSUNLOCKCLSTATE();
2929			    printf("DUP STATEID\n");
2930			    nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0,
2931				0);
2932			    return;
2933			}
2934		    }
2935		}
2936	    }
2937	}
2938
2939	/*
2940	 * Now search for duplicate opens.
2941	 * Duplicate opens for the same owner
2942	 * should never occur. Other duplicates are
2943	 * possible and are checked for if "dupopens"
2944	 * is true.
2945	 */
2946	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2947	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2948		if (nfhp->nfh_len == op2->nfso_fhlen &&
2949		    !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
2950		    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2951			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2952			    if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
2953				!NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
2954				(!NFSBCMP(op->nfso_own->nfsow_owner,
2955				 op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
2956				 dupopens)) {
2957				if (!NFSBCMP(op->nfso_own->nfsow_owner,
2958				    op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
2959				    NFSUNLOCKCLSTATE();
2960				    printf("BADDUP OPEN\n");
2961				} else {
2962				    NFSUNLOCKCLSTATE();
2963				    printf("DUP OPEN\n");
2964				}
2965				nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1,
2966				    0, 0);
2967				return;
2968			    }
2969			}
2970		    }
2971		}
2972	    }
2973	}
2974	NFSUNLOCKCLSTATE();
2975}
2976
2977/*
2978 * During close, find an open that needs to be dereferenced and
2979 * dereference it. If there are no more opens for this file,
2980 * log a message to that effect.
2981 * Opens aren't actually Close'd until VOP_INACTIVE() is performed
2982 * on the file's vnode.
2983 * This is the safe way, since it is difficult to identify
2984 * which open the close is for and I/O can be performed after the
2985 * close(2) system call when a file is mmap'd.
2986 * If it returns 0 for success, there will be a referenced
2987 * clp returned via clpp.
2988 */
2989APPLESTATIC int
2990nfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
2991{
2992	struct nfsclclient *clp;
2993	struct nfsclowner *owp;
2994	struct nfsclopen *op;
2995	struct nfscldeleg *dp;
2996	struct nfsfh *nfhp;
2997	int error, notdecr;
2998
2999	error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp);
3000	if (error)
3001		return (error);
3002	*clpp = clp;
3003
3004	nfhp = VTONFS(vp)->n_fhp;
3005	notdecr = 1;
3006	NFSLOCKCLSTATE();
3007	/*
3008	 * First, look for one under a delegation that was locally issued
3009	 * and just decrement the opencnt for it. Since all my Opens against
3010	 * the server are DENY_NONE, I don't see a problem with hanging
3011	 * onto them. (It is much easier to use one of the extant Opens
3012	 * that I already have on the server when a Delegation is recalled
3013	 * than to do fresh Opens.) Someday, I might need to rethink this, but.
3014	 */
3015	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3016	if (dp != NULL) {
3017		LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3018			op = LIST_FIRST(&owp->nfsow_open);
3019			if (op != NULL) {
3020				/*
3021				 * Since a delegation is for a file, there
3022				 * should never be more than one open for
3023				 * each openowner.
3024				 */
3025				if (LIST_NEXT(op, nfso_list) != NULL)
3026					panic("nfscdeleg opens");
3027				if (notdecr && op->nfso_opencnt > 0) {
3028					notdecr = 0;
3029					op->nfso_opencnt--;
3030					break;
3031				}
3032			}
3033		}
3034	}
3035
3036	/* Now process the opens against the server. */
3037	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3038		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3039			if (op->nfso_fhlen == nfhp->nfh_len &&
3040			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3041			    nfhp->nfh_len)) {
3042				/* Found an open, decrement cnt if possible */
3043				if (notdecr && op->nfso_opencnt > 0) {
3044					notdecr = 0;
3045					op->nfso_opencnt--;
3046				}
3047				/*
3048				 * There are more opens, so just return.
3049				 */
3050				if (op->nfso_opencnt > 0) {
3051					NFSUNLOCKCLSTATE();
3052					return (0);
3053				}
3054			}
3055		}
3056	}
3057	NFSUNLOCKCLSTATE();
3058	if (notdecr)
3059		printf("nfscl: never fnd open\n");
3060	return (0);
3061}
3062
3063APPLESTATIC int
3064nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
3065{
3066	struct nfsclclient *clp;
3067	struct nfsclowner *owp, *nowp;
3068	struct nfsclopen *op;
3069	struct nfscldeleg *dp;
3070	struct nfsfh *nfhp;
3071	int error;
3072
3073	error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp);
3074	if (error)
3075		return (error);
3076	*clpp = clp;
3077
3078	nfhp = VTONFS(vp)->n_fhp;
3079	NFSLOCKCLSTATE();
3080	/*
3081	 * First get rid of the local Open structures, which should be no
3082	 * longer in use.
3083	 */
3084	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3085	if (dp != NULL) {
3086		LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
3087			op = LIST_FIRST(&owp->nfsow_open);
3088			if (op != NULL) {
3089				KASSERT((op->nfso_opencnt == 0),
3090				    ("nfscl: bad open cnt on deleg"));
3091				nfscl_freeopen(op, 1);
3092			}
3093			nfscl_freeopenowner(owp, 1);
3094		}
3095	}
3096
3097	/* Return any layouts marked return on close. */
3098	nfscl_retoncloselayout(clp, nfhp->nfh_fh, nfhp->nfh_len);
3099
3100	/* Now process the opens against the server. */
3101lookformore:
3102	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3103		op = LIST_FIRST(&owp->nfsow_open);
3104		while (op != NULL) {
3105			if (op->nfso_fhlen == nfhp->nfh_len &&
3106			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3107			    nfhp->nfh_len)) {
3108				/* Found an open, close it. */
3109				KASSERT((op->nfso_opencnt == 0),
3110				    ("nfscl: bad open cnt on server"));
3111				NFSUNLOCKCLSTATE();
3112				nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op,
3113				    p);
3114				NFSLOCKCLSTATE();
3115				goto lookformore;
3116			}
3117			op = LIST_NEXT(op, nfso_list);
3118		}
3119	}
3120	NFSUNLOCKCLSTATE();
3121	return (0);
3122}
3123
3124/*
3125 * Return all delegations on this client.
3126 * (Must be called with client sleep lock.)
3127 */
3128static void
3129nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
3130{
3131	struct nfscldeleg *dp, *ndp;
3132	struct ucred *cred;
3133
3134	cred = newnfs_getcred();
3135	TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
3136		nfscl_cleandeleg(dp);
3137		(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
3138		nfscl_freedeleg(&clp->nfsc_deleg, dp);
3139	}
3140	NFSFREECRED(cred);
3141}
3142
3143/*
3144 * Do a callback RPC.
3145 */
3146APPLESTATIC void
3147nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
3148{
3149	int clist, gotseq_ok, i, j, k, op, rcalls;
3150	u_int32_t *tl;
3151	struct nfsclclient *clp;
3152	struct nfscldeleg *dp = NULL;
3153	int numops, taglen = -1, error = 0, trunc;
3154	u_int32_t minorvers = 0, retops = 0, *retopsp = NULL, *repp, cbident;
3155	u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
3156	vnode_t vp = NULL;
3157	struct nfsnode *np;
3158	struct vattr va;
3159	struct nfsfh *nfhp;
3160	mount_t mp;
3161	nfsattrbit_t attrbits, rattrbits;
3162	nfsv4stateid_t stateid;
3163	uint32_t seqid, slotid = 0, highslot, cachethis;
3164	uint8_t sessionid[NFSX_V4SESSIONID];
3165	struct mbuf *rep;
3166	struct nfscllayout *lyp;
3167	uint64_t filesid[2], len, off;
3168	int changed, gotone, laytype, recalltype;
3169	uint32_t iomode;
3170	struct nfsclrecalllayout *recallp = NULL;
3171	struct nfsclsession *tsep;
3172
3173	gotseq_ok = 0;
3174	nfsrvd_rephead(nd);
3175	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3176	taglen = fxdr_unsigned(int, *tl);
3177	if (taglen < 0) {
3178		error = EBADRPC;
3179		goto nfsmout;
3180	}
3181	if (taglen <= NFSV4_SMALLSTR)
3182		tagstr = tag;
3183	else
3184		tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
3185	error = nfsrv_mtostr(nd, tagstr, taglen);
3186	if (error) {
3187		if (taglen > NFSV4_SMALLSTR)
3188			free(tagstr, M_TEMP);
3189		taglen = -1;
3190		goto nfsmout;
3191	}
3192	(void) nfsm_strtom(nd, tag, taglen);
3193	if (taglen > NFSV4_SMALLSTR) {
3194		free(tagstr, M_TEMP);
3195	}
3196	NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
3197	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3198	minorvers = fxdr_unsigned(u_int32_t, *tl++);
3199	if (minorvers != NFSV4_MINORVERSION && minorvers != NFSV41_MINORVERSION)
3200		nd->nd_repstat = NFSERR_MINORVERMISMATCH;
3201	cbident = fxdr_unsigned(u_int32_t, *tl++);
3202	if (nd->nd_repstat)
3203		numops = 0;
3204	else
3205		numops = fxdr_unsigned(int, *tl);
3206	/*
3207	 * Loop around doing the sub ops.
3208	 */
3209	for (i = 0; i < numops; i++) {
3210		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3211		NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
3212		*repp++ = *tl;
3213		op = fxdr_unsigned(int, *tl);
3214		if (op < NFSV4OP_CBGETATTR ||
3215		   (op > NFSV4OP_CBRECALL && minorvers == NFSV4_MINORVERSION) ||
3216		   (op > NFSV4OP_CBNOTIFYDEVID &&
3217		    minorvers == NFSV41_MINORVERSION)) {
3218		    nd->nd_repstat = NFSERR_OPILLEGAL;
3219		    *repp = nfscl_errmap(nd, minorvers);
3220		    retops++;
3221		    break;
3222		}
3223		nd->nd_procnum = op;
3224		if (op < NFSV4OP_CBNOPS)
3225			newnfsstats.cbrpccnt[nd->nd_procnum]++;
3226		switch (op) {
3227		case NFSV4OP_CBGETATTR:
3228			NFSCL_DEBUG(4, "cbgetattr\n");
3229			mp = NULL;
3230			vp = NULL;
3231			error = nfsm_getfh(nd, &nfhp);
3232			if (!error)
3233				error = nfsrv_getattrbits(nd, &attrbits,
3234				    NULL, NULL);
3235			if (error == 0 && i == 0 &&
3236			    minorvers != NFSV4_MINORVERSION)
3237				error = NFSERR_OPNOTINSESS;
3238			if (!error) {
3239				mp = nfscl_getmnt(minorvers, sessionid, cbident,
3240				    &clp);
3241				if (mp == NULL)
3242					error = NFSERR_SERVERFAULT;
3243			}
3244			if (!error) {
3245				error = nfscl_ngetreopen(mp, nfhp->nfh_fh,
3246				    nfhp->nfh_len, p, &np);
3247				if (!error)
3248					vp = NFSTOV(np);
3249			}
3250			if (!error) {
3251				NFSZERO_ATTRBIT(&rattrbits);
3252				NFSLOCKCLSTATE();
3253				dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3254				    nfhp->nfh_len);
3255				if (dp != NULL) {
3256					if (NFSISSET_ATTRBIT(&attrbits,
3257					    NFSATTRBIT_SIZE)) {
3258						if (vp != NULL)
3259							va.va_size = np->n_size;
3260						else
3261							va.va_size =
3262							    dp->nfsdl_size;
3263						NFSSETBIT_ATTRBIT(&rattrbits,
3264						    NFSATTRBIT_SIZE);
3265					}
3266					if (NFSISSET_ATTRBIT(&attrbits,
3267					    NFSATTRBIT_CHANGE)) {
3268						va.va_filerev =
3269						    dp->nfsdl_change;
3270						if (vp == NULL ||
3271						    (np->n_flag & NDELEGMOD))
3272							va.va_filerev++;
3273						NFSSETBIT_ATTRBIT(&rattrbits,
3274						    NFSATTRBIT_CHANGE);
3275					}
3276				} else
3277					error = NFSERR_SERVERFAULT;
3278				NFSUNLOCKCLSTATE();
3279			}
3280			if (vp != NULL)
3281				vrele(vp);
3282			if (mp != NULL)
3283				vfs_unbusy(mp);
3284			if (nfhp != NULL)
3285				FREE((caddr_t)nfhp, M_NFSFH);
3286			if (!error)
3287				(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
3288				    NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0,
3289				    (uint64_t)0);
3290			break;
3291		case NFSV4OP_CBRECALL:
3292			NFSCL_DEBUG(4, "cbrecall\n");
3293			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
3294			    NFSX_UNSIGNED);
3295			stateid.seqid = *tl++;
3296			NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
3297			    NFSX_STATEIDOTHER);
3298			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3299			trunc = fxdr_unsigned(int, *tl);
3300			error = nfsm_getfh(nd, &nfhp);
3301			if (error == 0 && i == 0 &&
3302			    minorvers != NFSV4_MINORVERSION)
3303				error = NFSERR_OPNOTINSESS;
3304			if (!error) {
3305				NFSLOCKCLSTATE();
3306				if (minorvers == NFSV4_MINORVERSION)
3307					clp = nfscl_getclnt(cbident);
3308				else
3309					clp = nfscl_getclntsess(sessionid);
3310				if (clp != NULL) {
3311					dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3312					    nfhp->nfh_len);
3313					if (dp != NULL && (dp->nfsdl_flags &
3314					    NFSCLDL_DELEGRET) == 0) {
3315						dp->nfsdl_flags |=
3316						    NFSCLDL_RECALL;
3317						wakeup((caddr_t)clp);
3318					}
3319				} else {
3320					error = NFSERR_SERVERFAULT;
3321				}
3322				NFSUNLOCKCLSTATE();
3323			}
3324			if (nfhp != NULL)
3325				FREE((caddr_t)nfhp, M_NFSFH);
3326			break;
3327		case NFSV4OP_CBLAYOUTRECALL:
3328			NFSCL_DEBUG(4, "cblayrec\n");
3329			nfhp = NULL;
3330			NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
3331			laytype = fxdr_unsigned(int, *tl++);
3332			iomode = fxdr_unsigned(uint32_t, *tl++);
3333			if (newnfs_true == *tl++)
3334				changed = 1;
3335			else
3336				changed = 0;
3337			recalltype = fxdr_unsigned(int, *tl);
3338			recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL,
3339			    M_WAITOK);
3340			if (laytype != NFSLAYOUT_NFSV4_1_FILES)
3341				error = NFSERR_NOMATCHLAYOUT;
3342			else if (recalltype == NFSLAYOUTRETURN_FILE) {
3343				error = nfsm_getfh(nd, &nfhp);
3344				NFSCL_DEBUG(4, "retfile getfh=%d\n", error);
3345				if (error != 0)
3346					goto nfsmout;
3347				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER +
3348				    NFSX_STATEID);
3349				off = fxdr_hyper(tl); tl += 2;
3350				len = fxdr_hyper(tl); tl += 2;
3351				stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
3352				NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
3353				if (minorvers == NFSV4_MINORVERSION)
3354					error = NFSERR_NOTSUPP;
3355				else if (i == 0)
3356					error = NFSERR_OPNOTINSESS;
3357				if (error == 0) {
3358					NFSLOCKCLSTATE();
3359					clp = nfscl_getclntsess(sessionid);
3360					NFSCL_DEBUG(4, "cbly clp=%p\n", clp);
3361					if (clp != NULL) {
3362						lyp = nfscl_findlayout(clp,
3363						    nfhp->nfh_fh,
3364						    nfhp->nfh_len);
3365						NFSCL_DEBUG(4, "cblyp=%p\n",
3366						    lyp);
3367						if (lyp != NULL &&
3368						    (lyp->nfsly_flags &
3369						     NFSLY_FILES) != 0 &&
3370						    !NFSBCMP(stateid.other,
3371						    lyp->nfsly_stateid.other,
3372						    NFSX_STATEIDOTHER)) {
3373							error =
3374							    nfscl_layoutrecall(
3375							    recalltype,
3376							    lyp, iomode, off,
3377							    len, stateid.seqid,
3378							    recallp);
3379							recallp = NULL;
3380							wakeup(clp);
3381							NFSCL_DEBUG(4,
3382							    "aft layrcal=%d\n",
3383							    error);
3384						} else
3385							error =
3386							  NFSERR_NOMATCHLAYOUT;
3387					} else
3388						error = NFSERR_NOMATCHLAYOUT;
3389					NFSUNLOCKCLSTATE();
3390				}
3391				free(nfhp, M_NFSFH);
3392			} else if (recalltype == NFSLAYOUTRETURN_FSID) {
3393				NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER);
3394				filesid[0] = fxdr_hyper(tl); tl += 2;
3395				filesid[1] = fxdr_hyper(tl); tl += 2;
3396				gotone = 0;
3397				NFSLOCKCLSTATE();
3398				clp = nfscl_getclntsess(sessionid);
3399				if (clp != NULL) {
3400					TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3401					    nfsly_list) {
3402						if (lyp->nfsly_filesid[0] ==
3403						    filesid[0] &&
3404						    lyp->nfsly_filesid[1] ==
3405						    filesid[1]) {
3406							error =
3407							    nfscl_layoutrecall(
3408							    recalltype,
3409							    lyp, iomode, 0,
3410							    UINT64_MAX,
3411							    lyp->nfsly_stateid.seqid,
3412							    recallp);
3413							recallp = NULL;
3414							gotone = 1;
3415						}
3416					}
3417					if (gotone != 0)
3418						wakeup(clp);
3419					else
3420						error = NFSERR_NOMATCHLAYOUT;
3421				} else
3422					error = NFSERR_NOMATCHLAYOUT;
3423				NFSUNLOCKCLSTATE();
3424			} else if (recalltype == NFSLAYOUTRETURN_ALL) {
3425				gotone = 0;
3426				NFSLOCKCLSTATE();
3427				clp = nfscl_getclntsess(sessionid);
3428				if (clp != NULL) {
3429					TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3430					    nfsly_list) {
3431						error = nfscl_layoutrecall(
3432						    recalltype, lyp, iomode, 0,
3433						    UINT64_MAX,
3434						    lyp->nfsly_stateid.seqid,
3435						    recallp);
3436						recallp = NULL;
3437						gotone = 1;
3438					}
3439					if (gotone != 0)
3440						wakeup(clp);
3441					else
3442						error = NFSERR_NOMATCHLAYOUT;
3443				} else
3444					error = NFSERR_NOMATCHLAYOUT;
3445				NFSUNLOCKCLSTATE();
3446			} else
3447				error = NFSERR_NOMATCHLAYOUT;
3448			if (recallp != NULL) {
3449				free(recallp, M_NFSLAYRECALL);
3450				recallp = NULL;
3451			}
3452			break;
3453		case NFSV4OP_CBSEQUENCE:
3454			NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3455			    5 * NFSX_UNSIGNED);
3456			bcopy(tl, sessionid, NFSX_V4SESSIONID);
3457			tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3458			seqid = fxdr_unsigned(uint32_t, *tl++);
3459			slotid = fxdr_unsigned(uint32_t, *tl++);
3460			highslot = fxdr_unsigned(uint32_t, *tl++);
3461			cachethis = *tl++;
3462			/* Throw away the referring call stuff. */
3463			clist = fxdr_unsigned(int, *tl);
3464			for (j = 0; j < clist; j++) {
3465				NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3466				    NFSX_UNSIGNED);
3467				tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3468				rcalls = fxdr_unsigned(int, *tl);
3469				for (k = 0; k < rcalls; k++) {
3470					NFSM_DISSECT(tl, uint32_t *,
3471					    2 * NFSX_UNSIGNED);
3472				}
3473			}
3474			NFSLOCKCLSTATE();
3475			if (i == 0) {
3476				clp = nfscl_getclntsess(sessionid);
3477				if (clp == NULL)
3478					error = NFSERR_SERVERFAULT;
3479			} else
3480				error = NFSERR_SEQUENCEPOS;
3481			if (error == 0) {
3482				tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3483				error = nfsv4_seqsession(seqid, slotid,
3484				    highslot, tsep->nfsess_cbslots, &rep,
3485				    tsep->nfsess_backslots);
3486			}
3487			NFSUNLOCKCLSTATE();
3488			if (error == 0) {
3489				gotseq_ok = 1;
3490				if (rep != NULL) {
3491					NFSCL_DEBUG(4, "Got cbretry\n");
3492					m_freem(nd->nd_mreq);
3493					nd->nd_mreq = rep;
3494					rep = NULL;
3495					goto out;
3496				}
3497				NFSM_BUILD(tl, uint32_t *,
3498				    NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
3499				bcopy(sessionid, tl, NFSX_V4SESSIONID);
3500				tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3501				*tl++ = txdr_unsigned(seqid);
3502				*tl++ = txdr_unsigned(slotid);
3503				*tl++ = txdr_unsigned(NFSV4_CBSLOTS - 1);
3504				*tl = txdr_unsigned(NFSV4_CBSLOTS - 1);
3505			}
3506			break;
3507		default:
3508			if (i == 0 && minorvers == NFSV41_MINORVERSION)
3509				error = NFSERR_OPNOTINSESS;
3510			else {
3511				NFSCL_DEBUG(1, "unsupp callback %d\n", op);
3512				error = NFSERR_NOTSUPP;
3513			}
3514			break;
3515		};
3516		if (error) {
3517			if (error == EBADRPC || error == NFSERR_BADXDR) {
3518				nd->nd_repstat = NFSERR_BADXDR;
3519			} else {
3520				nd->nd_repstat = error;
3521			}
3522			error = 0;
3523		}
3524		retops++;
3525		if (nd->nd_repstat) {
3526			*repp = nfscl_errmap(nd, minorvers);
3527			break;
3528		} else
3529			*repp = 0;	/* NFS4_OK */
3530	}
3531nfsmout:
3532	if (recallp != NULL)
3533		free(recallp, M_NFSLAYRECALL);
3534	if (error) {
3535		if (error == EBADRPC || error == NFSERR_BADXDR)
3536			nd->nd_repstat = NFSERR_BADXDR;
3537		else
3538			printf("nfsv4 comperr1=%d\n", error);
3539	}
3540	if (taglen == -1) {
3541		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3542		*tl++ = 0;
3543		*tl = 0;
3544	} else {
3545		*retopsp = txdr_unsigned(retops);
3546	}
3547	*nd->nd_errp = nfscl_errmap(nd, minorvers);
3548out:
3549	if (gotseq_ok != 0) {
3550		rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK);
3551		NFSLOCKCLSTATE();
3552		clp = nfscl_getclntsess(sessionid);
3553		if (clp != NULL) {
3554			tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3555			nfsv4_seqsess_cacherep(slotid, tsep->nfsess_cbslots,
3556			    NFSERR_OK, &rep);
3557			NFSUNLOCKCLSTATE();
3558		} else {
3559			NFSUNLOCKCLSTATE();
3560			m_freem(rep);
3561		}
3562	}
3563}
3564
3565/*
3566 * Generate the next cbident value. Basically just increment a static value
3567 * and then check that it isn't already in the list, if it has wrapped around.
3568 */
3569static u_int32_t
3570nfscl_nextcbident(void)
3571{
3572	struct nfsclclient *clp;
3573	int matched;
3574	static u_int32_t nextcbident = 0;
3575	static int haswrapped = 0;
3576
3577	nextcbident++;
3578	if (nextcbident == 0)
3579		haswrapped = 1;
3580	if (haswrapped) {
3581		/*
3582		 * Search the clientid list for one already using this cbident.
3583		 */
3584		do {
3585			matched = 0;
3586			NFSLOCKCLSTATE();
3587			LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3588				if (clp->nfsc_cbident == nextcbident) {
3589					matched = 1;
3590					break;
3591				}
3592			}
3593			NFSUNLOCKCLSTATE();
3594			if (matched == 1)
3595				nextcbident++;
3596		} while (matched);
3597	}
3598	return (nextcbident);
3599}
3600
3601/*
3602 * Get the mount point related to a given cbident or session and busy it.
3603 */
3604static mount_t
3605nfscl_getmnt(int minorvers, uint8_t *sessionid, u_int32_t cbident,
3606    struct nfsclclient **clpp)
3607{
3608	struct nfsclclient *clp;
3609	mount_t mp;
3610	int error;
3611	struct nfsclsession *tsep;
3612
3613	*clpp = NULL;
3614	NFSLOCKCLSTATE();
3615	LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3616		tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3617		if (minorvers == NFSV4_MINORVERSION) {
3618			if (clp->nfsc_cbident == cbident)
3619				break;
3620		} else if (!NFSBCMP(tsep->nfsess_sessionid, sessionid,
3621		    NFSX_V4SESSIONID))
3622			break;
3623	}
3624	if (clp == NULL) {
3625		NFSUNLOCKCLSTATE();
3626		return (NULL);
3627	}
3628	mp = clp->nfsc_nmp->nm_mountp;
3629	vfs_ref(mp);
3630	NFSUNLOCKCLSTATE();
3631	error = vfs_busy(mp, 0);
3632	vfs_rel(mp);
3633	if (error != 0)
3634		return (NULL);
3635	*clpp = clp;
3636	return (mp);
3637}
3638
3639/*
3640 * Get the clientid pointer related to a given cbident.
3641 */
3642static struct nfsclclient *
3643nfscl_getclnt(u_int32_t cbident)
3644{
3645	struct nfsclclient *clp;
3646
3647	LIST_FOREACH(clp, &nfsclhead, nfsc_list)
3648		if (clp->nfsc_cbident == cbident)
3649			break;
3650	return (clp);
3651}
3652
3653/*
3654 * Get the clientid pointer related to a given sessionid.
3655 */
3656static struct nfsclclient *
3657nfscl_getclntsess(uint8_t *sessionid)
3658{
3659	struct nfsclclient *clp;
3660	struct nfsclsession *tsep;
3661
3662	LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3663		tsep = nfsmnt_mdssession(clp->nfsc_nmp);
3664		if (!NFSBCMP(tsep->nfsess_sessionid, sessionid,
3665		    NFSX_V4SESSIONID))
3666			break;
3667	}
3668	return (clp);
3669}
3670
3671/*
3672 * Search for a lock conflict locally on the client. A conflict occurs if
3673 * - not same owner and overlapping byte range and at least one of them is
3674 *   a write lock or this is an unlock.
3675 */
3676static int
3677nfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
3678    struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
3679    struct nfscllock **lopp)
3680{
3681	struct nfsclowner *owp;
3682	struct nfsclopen *op;
3683	int ret;
3684
3685	if (dp != NULL) {
3686		ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
3687		if (ret)
3688			return (ret);
3689	}
3690	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3691		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3692			if (op->nfso_fhlen == fhlen &&
3693			    !NFSBCMP(op->nfso_fh, fhp, fhlen)) {
3694				ret = nfscl_checkconflict(&op->nfso_lock, nlop,
3695				    own, lopp);
3696				if (ret)
3697					return (ret);
3698			}
3699		}
3700	}
3701	return (0);
3702}
3703
3704static int
3705nfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
3706    u_int8_t *own, struct nfscllock **lopp)
3707{
3708	struct nfscllockowner *lp;
3709	struct nfscllock *lop;
3710
3711	LIST_FOREACH(lp, lhp, nfsl_list) {
3712		if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
3713			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3714				if (lop->nfslo_first >= nlop->nfslo_end)
3715					break;
3716				if (lop->nfslo_end <= nlop->nfslo_first)
3717					continue;
3718				if (lop->nfslo_type == F_WRLCK ||
3719				    nlop->nfslo_type == F_WRLCK ||
3720				    nlop->nfslo_type == F_UNLCK) {
3721					if (lopp != NULL)
3722						*lopp = lop;
3723					return (NFSERR_DENIED);
3724				}
3725			}
3726		}
3727	}
3728	return (0);
3729}
3730
3731/*
3732 * Check for a local conflicting lock.
3733 */
3734APPLESTATIC int
3735nfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
3736    u_int64_t len, struct flock *fl, NFSPROC_T *p, void *id, int flags)
3737{
3738	struct nfscllock *lop, nlck;
3739	struct nfscldeleg *dp;
3740	struct nfsnode *np;
3741	u_int8_t own[NFSV4CL_LOCKNAMELEN];
3742	int error;
3743
3744	nlck.nfslo_type = fl->l_type;
3745	nlck.nfslo_first = off;
3746	if (len == NFS64BITSSET) {
3747		nlck.nfslo_end = NFS64BITSSET;
3748	} else {
3749		nlck.nfslo_end = off + len;
3750		if (nlck.nfslo_end <= nlck.nfslo_first)
3751			return (NFSERR_INVAL);
3752	}
3753	np = VTONFS(vp);
3754	nfscl_filllockowner(id, own, flags);
3755	NFSLOCKCLSTATE();
3756	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3757	error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
3758	    &nlck, own, dp, &lop);
3759	if (error != 0) {
3760		fl->l_whence = SEEK_SET;
3761		fl->l_start = lop->nfslo_first;
3762		if (lop->nfslo_end == NFS64BITSSET)
3763			fl->l_len = 0;
3764		else
3765			fl->l_len = lop->nfslo_end - lop->nfslo_first;
3766		fl->l_pid = (pid_t)0;
3767		fl->l_type = lop->nfslo_type;
3768		error = -1;			/* no RPC required */
3769	} else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
3770	    fl->l_type == F_RDLCK)) {
3771		/*
3772		 * The delegation ensures that there isn't a conflicting
3773		 * lock on the server, so return -1 to indicate an RPC
3774		 * isn't required.
3775		 */
3776		fl->l_type = F_UNLCK;
3777		error = -1;
3778	}
3779	NFSUNLOCKCLSTATE();
3780	return (error);
3781}
3782
3783/*
3784 * Handle Recall of a delegation.
3785 * The clp must be exclusive locked when this is called.
3786 */
3787static int
3788nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
3789    struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p,
3790    int called_from_renewthread)
3791{
3792	struct nfsclowner *owp, *lowp, *nowp;
3793	struct nfsclopen *op, *lop;
3794	struct nfscllockowner *lp;
3795	struct nfscllock *lckp;
3796	struct nfsnode *np;
3797	int error = 0, ret, gotvp = 0;
3798
3799	if (vp == NULL) {
3800		/*
3801		 * First, get a vnode for the file. This is needed to do RPCs.
3802		 */
3803		ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
3804		    dp->nfsdl_fhlen, p, &np);
3805		if (ret) {
3806			/*
3807			 * File isn't open, so nothing to move over to the
3808			 * server.
3809			 */
3810			return (0);
3811		}
3812		vp = NFSTOV(np);
3813		gotvp = 1;
3814	} else {
3815		np = VTONFS(vp);
3816	}
3817	dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
3818
3819	/*
3820	 * Ok, if it's a write delegation, flush data to the server, so
3821	 * that close/open consistency is retained.
3822	 */
3823	ret = 0;
3824	NFSLOCKNODE(np);
3825	if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
3826		np->n_flag |= NDELEGRECALL;
3827		NFSUNLOCKNODE(np);
3828		ret = ncl_flush(vp, MNT_WAIT, cred, p, 1,
3829		    called_from_renewthread);
3830		NFSLOCKNODE(np);
3831		np->n_flag &= ~NDELEGRECALL;
3832	}
3833	NFSINVALATTRCACHE(np);
3834	NFSUNLOCKNODE(np);
3835	if (ret == EIO && called_from_renewthread != 0) {
3836		/*
3837		 * If the flush failed with EIO for the renew thread,
3838		 * return now, so that the dirty buffer will be flushed
3839		 * later.
3840		 */
3841		if (gotvp != 0)
3842			vrele(vp);
3843		return (ret);
3844	}
3845
3846	/*
3847	 * Now, for each openowner with opens issued locally, move them
3848	 * over to state against the server.
3849	 */
3850	LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
3851		lop = LIST_FIRST(&lowp->nfsow_open);
3852		if (lop != NULL) {
3853			if (LIST_NEXT(lop, nfso_list) != NULL)
3854				panic("nfsdlg mult opens");
3855			/*
3856			 * Look for the same openowner against the server.
3857			 */
3858			LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3859				if (!NFSBCMP(lowp->nfsow_owner,
3860				    owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
3861					newnfs_copycred(&dp->nfsdl_cred, cred);
3862					ret = nfscl_moveopen(vp, clp, nmp, lop,
3863					    owp, dp, cred, p);
3864					if (ret == NFSERR_STALECLIENTID ||
3865					    ret == NFSERR_STALEDONTRECOVER ||
3866					    ret == NFSERR_BADSESSION) {
3867						if (gotvp)
3868							vrele(vp);
3869						return (ret);
3870					}
3871					if (ret) {
3872						nfscl_freeopen(lop, 1);
3873						if (!error)
3874							error = ret;
3875					}
3876					break;
3877				}
3878			}
3879
3880			/*
3881			 * If no openowner found, create one and get an open
3882			 * for it.
3883			 */
3884			if (owp == NULL) {
3885				MALLOC(nowp, struct nfsclowner *,
3886				    sizeof (struct nfsclowner), M_NFSCLOWNER,
3887				    M_WAITOK);
3888				nfscl_newopen(clp, NULL, &owp, &nowp, &op,
3889				    NULL, lowp->nfsow_owner, dp->nfsdl_fh,
3890				    dp->nfsdl_fhlen, NULL);
3891				newnfs_copycred(&dp->nfsdl_cred, cred);
3892				ret = nfscl_moveopen(vp, clp, nmp, lop,
3893				    owp, dp, cred, p);
3894				if (ret) {
3895					nfscl_freeopenowner(owp, 0);
3896					if (ret == NFSERR_STALECLIENTID ||
3897					    ret == NFSERR_STALEDONTRECOVER ||
3898					    ret == NFSERR_BADSESSION) {
3899						if (gotvp)
3900							vrele(vp);
3901						return (ret);
3902					}
3903					if (ret) {
3904						nfscl_freeopen(lop, 1);
3905						if (!error)
3906							error = ret;
3907					}
3908				}
3909			}
3910		}
3911	}
3912
3913	/*
3914	 * Now, get byte range locks for any locks done locally.
3915	 */
3916	LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3917		LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
3918			newnfs_copycred(&dp->nfsdl_cred, cred);
3919			ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
3920			if (ret == NFSERR_STALESTATEID ||
3921			    ret == NFSERR_STALEDONTRECOVER ||
3922			    ret == NFSERR_STALECLIENTID ||
3923			    ret == NFSERR_BADSESSION) {
3924				if (gotvp)
3925					vrele(vp);
3926				return (ret);
3927			}
3928			if (ret && !error)
3929				error = ret;
3930		}
3931	}
3932	if (gotvp)
3933		vrele(vp);
3934	return (error);
3935}
3936
3937/*
3938 * Move a locally issued open over to an owner on the state list.
3939 * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
3940 * returns with it unlocked.
3941 */
3942static int
3943nfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3944    struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
3945    struct ucred *cred, NFSPROC_T *p)
3946{
3947	struct nfsclopen *op, *nop;
3948	struct nfscldeleg *ndp;
3949	struct nfsnode *np;
3950	int error = 0, newone;
3951
3952	/*
3953	 * First, look for an appropriate open, If found, just increment the
3954	 * opencnt in it.
3955	 */
3956	LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3957		if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
3958		    op->nfso_fhlen == lop->nfso_fhlen &&
3959		    !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
3960			op->nfso_opencnt += lop->nfso_opencnt;
3961			nfscl_freeopen(lop, 1);
3962			return (0);
3963		}
3964	}
3965
3966	/* No appropriate open, so we have to do one against the server. */
3967	np = VTONFS(vp);
3968	MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
3969	    lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
3970	newone = 0;
3971	nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
3972	    lop->nfso_fh, lop->nfso_fhlen, &newone);
3973	ndp = dp;
3974	error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen,
3975	    lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
3976	    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
3977	if (error) {
3978		if (newone)
3979			nfscl_freeopen(op, 0);
3980	} else {
3981		if (newone)
3982			newnfs_copyincred(cred, &op->nfso_cred);
3983		op->nfso_mode |= lop->nfso_mode;
3984		op->nfso_opencnt += lop->nfso_opencnt;
3985		nfscl_freeopen(lop, 1);
3986	}
3987	if (nop != NULL)
3988		FREE((caddr_t)nop, M_NFSCLOPEN);
3989	if (ndp != NULL) {
3990		/*
3991		 * What should I do with the returned delegation, since the
3992		 * delegation is being recalled? For now, just printf and
3993		 * through it away.
3994		 */
3995		printf("Moveopen returned deleg\n");
3996		FREE((caddr_t)ndp, M_NFSCLDELEG);
3997	}
3998	return (error);
3999}
4000
4001/*
4002 * Recall all delegations on this client.
4003 */
4004static void
4005nfscl_totalrecall(struct nfsclclient *clp)
4006{
4007	struct nfscldeleg *dp;
4008
4009	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
4010		if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0)
4011			dp->nfsdl_flags |= NFSCLDL_RECALL;
4012	}
4013}
4014
4015/*
4016 * Relock byte ranges. Called for delegation recall and state expiry.
4017 */
4018static int
4019nfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
4020    struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
4021    NFSPROC_T *p)
4022{
4023	struct nfscllockowner *nlp;
4024	struct nfsfh *nfhp;
4025	u_int64_t off, len;
4026	u_int32_t clidrev = 0;
4027	int error, newone, donelocally;
4028
4029	off = lop->nfslo_first;
4030	len = lop->nfslo_end - lop->nfslo_first;
4031	error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
4032	    clp, 1, NULL, lp->nfsl_lockflags, lp->nfsl_owner,
4033	    lp->nfsl_openowner, &nlp, &newone, &donelocally);
4034	if (error || donelocally)
4035		return (error);
4036	if (nmp->nm_clp != NULL)
4037		clidrev = nmp->nm_clp->nfsc_clientidrev;
4038	else
4039		clidrev = 0;
4040	nfhp = VTONFS(vp)->n_fhp;
4041	error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
4042	    nfhp->nfh_len, nlp, newone, 0, off,
4043	    len, lop->nfslo_type, cred, p);
4044	if (error)
4045		nfscl_freelockowner(nlp, 0);
4046	return (error);
4047}
4048
4049/*
4050 * Called to re-open a file. Basically get a vnode for the file handle
4051 * and then call nfsrpc_openrpc() to do the rest.
4052 */
4053static int
4054nfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
4055    u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
4056    struct ucred *cred, NFSPROC_T *p)
4057{
4058	struct nfsnode *np;
4059	vnode_t vp;
4060	int error;
4061
4062	error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
4063	if (error)
4064		return (error);
4065	vp = NFSTOV(np);
4066	if (np->n_v4 != NULL) {
4067		error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
4068		    np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
4069		    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
4070		    cred, p);
4071	} else {
4072		error = EINVAL;
4073	}
4074	vrele(vp);
4075	return (error);
4076}
4077
4078/*
4079 * Try an open against the server. Just call nfsrpc_openrpc(), retrying while
4080 * NFSERR_DELAY. Also, try system credentials, if the passed in credentials
4081 * fail.
4082 */
4083static int
4084nfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4085    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
4086    u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
4087    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
4088{
4089	int error;
4090
4091	do {
4092		error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
4093		    mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
4094		    0, 0);
4095		if (error == NFSERR_DELAY)
4096			(void) nfs_catnap(PZERO, error, "nfstryop");
4097	} while (error == NFSERR_DELAY);
4098	if (error == EAUTH || error == EACCES) {
4099		/* Try again using system credentials */
4100		newnfs_setroot(cred);
4101		do {
4102		    error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
4103			newfhlen, mode, op, name, namelen, ndpp, reclaim,
4104			delegtype, cred, p, 1, 0);
4105		    if (error == NFSERR_DELAY)
4106			(void) nfs_catnap(PZERO, error, "nfstryop");
4107		} while (error == NFSERR_DELAY);
4108	}
4109	return (error);
4110}
4111
4112/*
4113 * Try a byte range lock. Just loop on nfsrpc_lock() while it returns
4114 * NFSERR_DELAY. Also, retry with system credentials, if the provided
4115 * cred don't work.
4116 */
4117static int
4118nfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
4119    int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
4120    u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
4121{
4122	struct nfsrv_descript nfsd, *nd = &nfsd;
4123	int error;
4124
4125	do {
4126		error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
4127		    reclaim, off, len, type, cred, p, 0);
4128		if (!error && nd->nd_repstat == NFSERR_DELAY)
4129			(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4130			    "nfstrylck");
4131	} while (!error && nd->nd_repstat == NFSERR_DELAY);
4132	if (!error)
4133		error = nd->nd_repstat;
4134	if (error == EAUTH || error == EACCES) {
4135		/* Try again using root credentials */
4136		newnfs_setroot(cred);
4137		do {
4138			error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
4139			    newone, reclaim, off, len, type, cred, p, 1);
4140			if (!error && nd->nd_repstat == NFSERR_DELAY)
4141				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4142				    "nfstrylck");
4143		} while (!error && nd->nd_repstat == NFSERR_DELAY);
4144		if (!error)
4145			error = nd->nd_repstat;
4146	}
4147	return (error);
4148}
4149
4150/*
4151 * Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
4152 * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4153 * credentials fail.
4154 */
4155static int
4156nfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
4157    struct nfsmount *nmp, NFSPROC_T *p)
4158{
4159	int error;
4160
4161	do {
4162		error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
4163		if (error == NFSERR_DELAY)
4164			(void) nfs_catnap(PZERO, error, "nfstrydp");
4165	} while (error == NFSERR_DELAY);
4166	if (error == EAUTH || error == EACCES) {
4167		/* Try again using system credentials */
4168		newnfs_setroot(cred);
4169		do {
4170			error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
4171			if (error == NFSERR_DELAY)
4172				(void) nfs_catnap(PZERO, error, "nfstrydp");
4173		} while (error == NFSERR_DELAY);
4174	}
4175	return (error);
4176}
4177
4178/*
4179 * Try a close against the server. Just call nfsrpc_closerpc(),
4180 * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4181 * credentials fail.
4182 */
4183APPLESTATIC int
4184nfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
4185    struct nfsmount *nmp, NFSPROC_T *p)
4186{
4187	struct nfsrv_descript nfsd, *nd = &nfsd;
4188	int error;
4189
4190	do {
4191		error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
4192		if (error == NFSERR_DELAY)
4193			(void) nfs_catnap(PZERO, error, "nfstrycl");
4194	} while (error == NFSERR_DELAY);
4195	if (error == EAUTH || error == EACCES) {
4196		/* Try again using system credentials */
4197		newnfs_setroot(cred);
4198		do {
4199			error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
4200			if (error == NFSERR_DELAY)
4201				(void) nfs_catnap(PZERO, error, "nfstrycl");
4202		} while (error == NFSERR_DELAY);
4203	}
4204	return (error);
4205}
4206
4207/*
4208 * Decide if a delegation on a file permits close without flushing writes
4209 * to the server. This might be a big performance win in some environments.
4210 * (Not useful until the client does caching on local stable storage.)
4211 */
4212APPLESTATIC int
4213nfscl_mustflush(vnode_t vp)
4214{
4215	struct nfsclclient *clp;
4216	struct nfscldeleg *dp;
4217	struct nfsnode *np;
4218	struct nfsmount *nmp;
4219
4220	np = VTONFS(vp);
4221	nmp = VFSTONFS(vnode_mount(vp));
4222	if (!NFSHASNFSV4(nmp))
4223		return (1);
4224	NFSLOCKCLSTATE();
4225	clp = nfscl_findcl(nmp);
4226	if (clp == NULL) {
4227		NFSUNLOCKCLSTATE();
4228		return (1);
4229	}
4230	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4231	if (dp != NULL && (dp->nfsdl_flags &
4232	    (NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) ==
4233	     NFSCLDL_WRITE &&
4234	    (dp->nfsdl_sizelimit >= np->n_size ||
4235	     !NFSHASSTRICT3530(nmp))) {
4236		NFSUNLOCKCLSTATE();
4237		return (0);
4238	}
4239	NFSUNLOCKCLSTATE();
4240	return (1);
4241}
4242
4243/*
4244 * See if a (write) delegation exists for this file.
4245 */
4246APPLESTATIC int
4247nfscl_nodeleg(vnode_t vp, int writedeleg)
4248{
4249	struct nfsclclient *clp;
4250	struct nfscldeleg *dp;
4251	struct nfsnode *np;
4252	struct nfsmount *nmp;
4253
4254	np = VTONFS(vp);
4255	nmp = VFSTONFS(vnode_mount(vp));
4256	if (!NFSHASNFSV4(nmp))
4257		return (1);
4258	NFSLOCKCLSTATE();
4259	clp = nfscl_findcl(nmp);
4260	if (clp == NULL) {
4261		NFSUNLOCKCLSTATE();
4262		return (1);
4263	}
4264	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4265	if (dp != NULL &&
4266	    (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 &&
4267	    (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) ==
4268	     NFSCLDL_WRITE)) {
4269		NFSUNLOCKCLSTATE();
4270		return (0);
4271	}
4272	NFSUNLOCKCLSTATE();
4273	return (1);
4274}
4275
4276/*
4277 * Look for an associated delegation that should be DelegReturned.
4278 */
4279APPLESTATIC int
4280nfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
4281{
4282	struct nfsclclient *clp;
4283	struct nfscldeleg *dp;
4284	struct nfsclowner *owp;
4285	struct nfscllockowner *lp;
4286	struct nfsmount *nmp;
4287	struct ucred *cred;
4288	struct nfsnode *np;
4289	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4290
4291	nmp = VFSTONFS(vnode_mount(vp));
4292	np = VTONFS(vp);
4293	NFSLOCKCLSTATE();
4294	/*
4295	 * Loop around waiting for:
4296	 * - outstanding I/O operations on delegations to complete
4297	 * - for a delegation on vp that has state, lock the client and
4298	 *   do a recall
4299	 * - return delegation with no state
4300	 */
4301	while (1) {
4302		clp = nfscl_findcl(nmp);
4303		if (clp == NULL) {
4304			NFSUNLOCKCLSTATE();
4305			return (retcnt);
4306		}
4307		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4308		    np->n_fhp->nfh_len);
4309		if (dp != NULL) {
4310		    /*
4311		     * Wait for outstanding I/O ops to be done.
4312		     */
4313		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4314			if (igotlock) {
4315			    nfsv4_unlock(&clp->nfsc_lock, 0);
4316			    igotlock = 0;
4317			}
4318			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4319			(void) nfsmsleep(&dp->nfsdl_rwlock,
4320			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4321			continue;
4322		    }
4323		    needsrecall = 0;
4324		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4325			if (!LIST_EMPTY(&owp->nfsow_open)) {
4326			    needsrecall = 1;
4327			    break;
4328			}
4329		    }
4330		    if (!needsrecall) {
4331			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4332			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4333				needsrecall = 1;
4334				break;
4335			    }
4336			}
4337		    }
4338		    if (needsrecall && !triedrecall) {
4339			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4340			islept = 0;
4341			while (!igotlock) {
4342			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4343				&islept, NFSCLSTATEMUTEXPTR, NULL);
4344			    if (islept)
4345				break;
4346			}
4347			if (islept)
4348			    continue;
4349			NFSUNLOCKCLSTATE();
4350			cred = newnfs_getcred();
4351			newnfs_copycred(&dp->nfsdl_cred, cred);
4352			(void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0);
4353			NFSFREECRED(cred);
4354			triedrecall = 1;
4355			NFSLOCKCLSTATE();
4356			nfsv4_unlock(&clp->nfsc_lock, 0);
4357			igotlock = 0;
4358			continue;
4359		    }
4360		    *stp = dp->nfsdl_stateid;
4361		    retcnt = 1;
4362		    nfscl_cleandeleg(dp);
4363		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4364		}
4365		if (igotlock)
4366		    nfsv4_unlock(&clp->nfsc_lock, 0);
4367		NFSUNLOCKCLSTATE();
4368		return (retcnt);
4369	}
4370}
4371
4372/*
4373 * Look for associated delegation(s) that should be DelegReturned.
4374 */
4375APPLESTATIC int
4376nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
4377    nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
4378{
4379	struct nfsclclient *clp;
4380	struct nfscldeleg *dp;
4381	struct nfsclowner *owp;
4382	struct nfscllockowner *lp;
4383	struct nfsmount *nmp;
4384	struct ucred *cred;
4385	struct nfsnode *np;
4386	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4387
4388	nmp = VFSTONFS(vnode_mount(fvp));
4389	*gotfdp = 0;
4390	*gottdp = 0;
4391	NFSLOCKCLSTATE();
4392	/*
4393	 * Loop around waiting for:
4394	 * - outstanding I/O operations on delegations to complete
4395	 * - for a delegation on fvp that has state, lock the client and
4396	 *   do a recall
4397	 * - return delegation(s) with no state.
4398	 */
4399	while (1) {
4400		clp = nfscl_findcl(nmp);
4401		if (clp == NULL) {
4402			NFSUNLOCKCLSTATE();
4403			return (retcnt);
4404		}
4405		np = VTONFS(fvp);
4406		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4407		    np->n_fhp->nfh_len);
4408		if (dp != NULL && *gotfdp == 0) {
4409		    /*
4410		     * Wait for outstanding I/O ops to be done.
4411		     */
4412		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4413			if (igotlock) {
4414			    nfsv4_unlock(&clp->nfsc_lock, 0);
4415			    igotlock = 0;
4416			}
4417			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4418			(void) nfsmsleep(&dp->nfsdl_rwlock,
4419			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4420			continue;
4421		    }
4422		    needsrecall = 0;
4423		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4424			if (!LIST_EMPTY(&owp->nfsow_open)) {
4425			    needsrecall = 1;
4426			    break;
4427			}
4428		    }
4429		    if (!needsrecall) {
4430			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4431			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4432				needsrecall = 1;
4433				break;
4434			    }
4435			}
4436		    }
4437		    if (needsrecall && !triedrecall) {
4438			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4439			islept = 0;
4440			while (!igotlock) {
4441			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4442				&islept, NFSCLSTATEMUTEXPTR, NULL);
4443			    if (islept)
4444				break;
4445			}
4446			if (islept)
4447			    continue;
4448			NFSUNLOCKCLSTATE();
4449			cred = newnfs_getcred();
4450			newnfs_copycred(&dp->nfsdl_cred, cred);
4451			(void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0);
4452			NFSFREECRED(cred);
4453			triedrecall = 1;
4454			NFSLOCKCLSTATE();
4455			nfsv4_unlock(&clp->nfsc_lock, 0);
4456			igotlock = 0;
4457			continue;
4458		    }
4459		    *fstp = dp->nfsdl_stateid;
4460		    retcnt++;
4461		    *gotfdp = 1;
4462		    nfscl_cleandeleg(dp);
4463		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4464		}
4465		if (igotlock) {
4466		    nfsv4_unlock(&clp->nfsc_lock, 0);
4467		    igotlock = 0;
4468		}
4469		if (tvp != NULL) {
4470		    np = VTONFS(tvp);
4471		    dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4472			np->n_fhp->nfh_len);
4473		    if (dp != NULL && *gottdp == 0) {
4474			/*
4475			 * Wait for outstanding I/O ops to be done.
4476			 */
4477			if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4478			    dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4479			    (void) nfsmsleep(&dp->nfsdl_rwlock,
4480				NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4481			    continue;
4482			}
4483			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4484			    if (!LIST_EMPTY(&owp->nfsow_open)) {
4485				NFSUNLOCKCLSTATE();
4486				return (retcnt);
4487			    }
4488			}
4489			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4490			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4491				NFSUNLOCKCLSTATE();
4492				return (retcnt);
4493			    }
4494			}
4495			*tstp = dp->nfsdl_stateid;
4496			retcnt++;
4497			*gottdp = 1;
4498			nfscl_cleandeleg(dp);
4499			nfscl_freedeleg(&clp->nfsc_deleg, dp);
4500		    }
4501		}
4502		NFSUNLOCKCLSTATE();
4503		return (retcnt);
4504	}
4505}
4506
4507/*
4508 * Get a reference on the clientid associated with the mount point.
4509 * Return 1 if success, 0 otherwise.
4510 */
4511APPLESTATIC int
4512nfscl_getref(struct nfsmount *nmp)
4513{
4514	struct nfsclclient *clp;
4515
4516	NFSLOCKCLSTATE();
4517	clp = nfscl_findcl(nmp);
4518	if (clp == NULL) {
4519		NFSUNLOCKCLSTATE();
4520		return (0);
4521	}
4522	nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL);
4523	NFSUNLOCKCLSTATE();
4524	return (1);
4525}
4526
4527/*
4528 * Release a reference on a clientid acquired with the above call.
4529 */
4530APPLESTATIC void
4531nfscl_relref(struct nfsmount *nmp)
4532{
4533	struct nfsclclient *clp;
4534
4535	NFSLOCKCLSTATE();
4536	clp = nfscl_findcl(nmp);
4537	if (clp == NULL) {
4538		NFSUNLOCKCLSTATE();
4539		return;
4540	}
4541	nfsv4_relref(&clp->nfsc_lock);
4542	NFSUNLOCKCLSTATE();
4543}
4544
4545/*
4546 * Save the size attribute in the delegation, since the nfsnode
4547 * is going away.
4548 */
4549APPLESTATIC void
4550nfscl_reclaimnode(vnode_t vp)
4551{
4552	struct nfsclclient *clp;
4553	struct nfscldeleg *dp;
4554	struct nfsnode *np = VTONFS(vp);
4555	struct nfsmount *nmp;
4556
4557	nmp = VFSTONFS(vnode_mount(vp));
4558	if (!NFSHASNFSV4(nmp))
4559		return;
4560	NFSLOCKCLSTATE();
4561	clp = nfscl_findcl(nmp);
4562	if (clp == NULL) {
4563		NFSUNLOCKCLSTATE();
4564		return;
4565	}
4566	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4567	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4568		dp->nfsdl_size = np->n_size;
4569	NFSUNLOCKCLSTATE();
4570}
4571
4572/*
4573 * Get the saved size attribute in the delegation, since it is a
4574 * newly allocated nfsnode.
4575 */
4576APPLESTATIC void
4577nfscl_newnode(vnode_t vp)
4578{
4579	struct nfsclclient *clp;
4580	struct nfscldeleg *dp;
4581	struct nfsnode *np = VTONFS(vp);
4582	struct nfsmount *nmp;
4583
4584	nmp = VFSTONFS(vnode_mount(vp));
4585	if (!NFSHASNFSV4(nmp))
4586		return;
4587	NFSLOCKCLSTATE();
4588	clp = nfscl_findcl(nmp);
4589	if (clp == NULL) {
4590		NFSUNLOCKCLSTATE();
4591		return;
4592	}
4593	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4594	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4595		np->n_size = dp->nfsdl_size;
4596	NFSUNLOCKCLSTATE();
4597}
4598
4599/*
4600 * If there is a valid write delegation for this file, set the modtime
4601 * to the local clock time.
4602 */
4603APPLESTATIC void
4604nfscl_delegmodtime(vnode_t vp)
4605{
4606	struct nfsclclient *clp;
4607	struct nfscldeleg *dp;
4608	struct nfsnode *np = VTONFS(vp);
4609	struct nfsmount *nmp;
4610
4611	nmp = VFSTONFS(vnode_mount(vp));
4612	if (!NFSHASNFSV4(nmp))
4613		return;
4614	NFSLOCKCLSTATE();
4615	clp = nfscl_findcl(nmp);
4616	if (clp == NULL) {
4617		NFSUNLOCKCLSTATE();
4618		return;
4619	}
4620	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4621	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
4622		nanotime(&dp->nfsdl_modtime);
4623		dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
4624	}
4625	NFSUNLOCKCLSTATE();
4626}
4627
4628/*
4629 * If there is a valid write delegation for this file with a modtime set,
4630 * put that modtime in mtime.
4631 */
4632APPLESTATIC void
4633nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
4634{
4635	struct nfsclclient *clp;
4636	struct nfscldeleg *dp;
4637	struct nfsnode *np = VTONFS(vp);
4638	struct nfsmount *nmp;
4639
4640	nmp = VFSTONFS(vnode_mount(vp));
4641	if (!NFSHASNFSV4(nmp))
4642		return;
4643	NFSLOCKCLSTATE();
4644	clp = nfscl_findcl(nmp);
4645	if (clp == NULL) {
4646		NFSUNLOCKCLSTATE();
4647		return;
4648	}
4649	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4650	if (dp != NULL &&
4651	    (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
4652	    (NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
4653		*mtime = dp->nfsdl_modtime;
4654	NFSUNLOCKCLSTATE();
4655}
4656
4657static int
4658nfscl_errmap(struct nfsrv_descript *nd, u_int32_t minorvers)
4659{
4660	short *defaulterrp, *errp;
4661
4662	if (!nd->nd_repstat)
4663		return (0);
4664	if (nd->nd_procnum == NFSPROC_NOOP)
4665		return (txdr_unsigned(nd->nd_repstat & 0xffff));
4666	if (nd->nd_repstat == EBADRPC)
4667		return (txdr_unsigned(NFSERR_BADXDR));
4668	if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
4669	    nd->nd_repstat == NFSERR_OPILLEGAL)
4670		return (txdr_unsigned(nd->nd_repstat));
4671	if (nd->nd_repstat >= NFSERR_BADIOMODE && nd->nd_repstat < 20000 &&
4672	    minorvers > NFSV4_MINORVERSION) {
4673		/* NFSv4.n error. */
4674		return (txdr_unsigned(nd->nd_repstat));
4675	}
4676	if (nd->nd_procnum < NFSV4OP_CBNOPS)
4677		errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
4678	else
4679		return (txdr_unsigned(nd->nd_repstat));
4680	while (*++errp)
4681		if (*errp == (short)nd->nd_repstat)
4682			return (txdr_unsigned(nd->nd_repstat));
4683	return (txdr_unsigned(*defaulterrp));
4684}
4685
4686/*
4687 * Called to find/add a layout to a client.
4688 * This function returns the layout with a refcnt (shared lock) upon
4689 * success (returns 0) or with no lock/refcnt on the layout when an
4690 * error is returned.
4691 * If a layout is passed in via lypp, it is locked (exclusively locked).
4692 */
4693APPLESTATIC int
4694nfscl_layout(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4695    nfsv4stateid_t *stateidp, int retonclose,
4696    struct nfsclflayouthead *fhlp, struct nfscllayout **lypp,
4697    struct ucred *cred, NFSPROC_T *p)
4698{
4699	struct nfsclclient *clp;
4700	struct nfscllayout *lyp, *tlyp;
4701	struct nfsclflayout *flp;
4702	struct nfsnode *np = VTONFS(vp);
4703	mount_t mp;
4704	int layout_passed_in;
4705
4706	mp = nmp->nm_mountp;
4707	layout_passed_in = 1;
4708	tlyp = NULL;
4709	lyp = *lypp;
4710	if (lyp == NULL) {
4711		layout_passed_in = 0;
4712		tlyp = malloc(sizeof(*tlyp) + fhlen - 1, M_NFSLAYOUT,
4713		    M_WAITOK | M_ZERO);
4714	}
4715
4716	NFSLOCKCLSTATE();
4717	clp = nmp->nm_clp;
4718	if (clp == NULL) {
4719		if (layout_passed_in != 0)
4720			nfsv4_unlock(&lyp->nfsly_lock, 0);
4721		NFSUNLOCKCLSTATE();
4722		if (tlyp != NULL)
4723			free(tlyp, M_NFSLAYOUT);
4724		return (EPERM);
4725	}
4726	if (lyp == NULL) {
4727		/*
4728		 * Although no lyp was passed in, another thread might have
4729		 * allocated one. If one is found, just increment it's ref
4730		 * count and return it.
4731		 */
4732		lyp = nfscl_findlayout(clp, fhp, fhlen);
4733		if (lyp == NULL) {
4734			lyp = tlyp;
4735			tlyp = NULL;
4736			lyp->nfsly_stateid.seqid = stateidp->seqid;
4737			lyp->nfsly_stateid.other[0] = stateidp->other[0];
4738			lyp->nfsly_stateid.other[1] = stateidp->other[1];
4739			lyp->nfsly_stateid.other[2] = stateidp->other[2];
4740			lyp->nfsly_lastbyte = 0;
4741			LIST_INIT(&lyp->nfsly_flayread);
4742			LIST_INIT(&lyp->nfsly_flayrw);
4743			LIST_INIT(&lyp->nfsly_recall);
4744			lyp->nfsly_filesid[0] = np->n_vattr.na_filesid[0];
4745			lyp->nfsly_filesid[1] = np->n_vattr.na_filesid[1];
4746			lyp->nfsly_clp = clp;
4747			lyp->nfsly_flags = (retonclose != 0) ?
4748			    (NFSLY_FILES | NFSLY_RETONCLOSE) : NFSLY_FILES;
4749			lyp->nfsly_fhlen = fhlen;
4750			NFSBCOPY(fhp, lyp->nfsly_fh, fhlen);
4751			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4752			LIST_INSERT_HEAD(NFSCLLAYOUTHASH(clp, fhp, fhlen), lyp,
4753			    nfsly_hash);
4754			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4755			nfscl_layoutcnt++;
4756		} else {
4757			if (retonclose != 0)
4758				lyp->nfsly_flags |= NFSLY_RETONCLOSE;
4759			TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
4760			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4761			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4762		}
4763		nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
4764		if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
4765			NFSUNLOCKCLSTATE();
4766			if (tlyp != NULL)
4767				free(tlyp, M_NFSLAYOUT);
4768			return (EPERM);
4769		}
4770		*lypp = lyp;
4771	} else
4772		lyp->nfsly_stateid.seqid = stateidp->seqid;
4773
4774	/* Merge the new list of File Layouts into the list. */
4775	flp = LIST_FIRST(fhlp);
4776	if (flp != NULL) {
4777		if (flp->nfsfl_iomode == NFSLAYOUTIOMODE_READ)
4778			nfscl_mergeflayouts(&lyp->nfsly_flayread, fhlp);
4779		else
4780			nfscl_mergeflayouts(&lyp->nfsly_flayrw, fhlp);
4781	}
4782	if (layout_passed_in != 0)
4783		nfsv4_unlock(&lyp->nfsly_lock, 1);
4784	NFSUNLOCKCLSTATE();
4785	if (tlyp != NULL)
4786		free(tlyp, M_NFSLAYOUT);
4787	return (0);
4788}
4789
4790/*
4791 * Search for a layout by MDS file handle.
4792 * If one is found, it is returned with a refcnt (shared lock) iff
4793 * retflpp returned non-NULL and locked (exclusive locked) iff retflpp is
4794 * returned NULL.
4795 */
4796struct nfscllayout *
4797nfscl_getlayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen,
4798    uint64_t off, struct nfsclflayout **retflpp, int *recalledp)
4799{
4800	struct nfscllayout *lyp;
4801	mount_t mp;
4802	int error, igotlock;
4803
4804	mp = clp->nfsc_nmp->nm_mountp;
4805	*recalledp = 0;
4806	*retflpp = NULL;
4807	NFSLOCKCLSTATE();
4808	lyp = nfscl_findlayout(clp, fhp, fhlen);
4809	if (lyp != NULL) {
4810		if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) {
4811			TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
4812			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4813			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4814			error = nfscl_findlayoutforio(lyp, off,
4815			    NFSV4OPEN_ACCESSREAD, retflpp);
4816			if (error == 0)
4817				nfsv4_getref(&lyp->nfsly_lock, NULL,
4818				    NFSCLSTATEMUTEXPTR, mp);
4819			else {
4820				do {
4821					igotlock = nfsv4_lock(&lyp->nfsly_lock,
4822					    1, NULL, NFSCLSTATEMUTEXPTR, mp);
4823				} while (igotlock == 0 &&
4824				    (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0);
4825				*retflpp = NULL;
4826			}
4827			if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
4828				lyp = NULL;
4829				*recalledp = 1;
4830			}
4831		} else {
4832			lyp = NULL;
4833			*recalledp = 1;
4834		}
4835	}
4836	NFSUNLOCKCLSTATE();
4837	return (lyp);
4838}
4839
4840/*
4841 * Search for a layout by MDS file handle. If one is found that is marked
4842 * "return on close", delete it, since it should now be forgotten.
4843 */
4844static void
4845nfscl_retoncloselayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen)
4846{
4847	struct nfscllayout *lyp;
4848
4849tryagain:
4850	lyp = nfscl_findlayout(clp, fhp, fhlen);
4851	if (lyp != NULL && (lyp->nfsly_flags & NFSLY_RETONCLOSE) != 0) {
4852		/*
4853		 * Wait for outstanding I/O ops to be done.
4854		 */
4855		if (lyp->nfsly_lock.nfslock_usecnt != 0 ||
4856		    lyp->nfsly_lock.nfslock_lock != 0) {
4857			lyp->nfsly_lock.nfslock_lock |= NFSV4LOCK_WANTED;
4858			(void)mtx_sleep(&lyp->nfsly_lock,
4859			    NFSCLSTATEMUTEXPTR, PZERO, "nfslyc", 0);
4860			goto tryagain;
4861		}
4862		nfscl_freelayout(lyp);
4863	}
4864}
4865
4866/*
4867 * Dereference a layout.
4868 */
4869void
4870nfscl_rellayout(struct nfscllayout *lyp, int exclocked)
4871{
4872
4873	NFSLOCKCLSTATE();
4874	if (exclocked != 0)
4875		nfsv4_unlock(&lyp->nfsly_lock, 0);
4876	else
4877		nfsv4_relref(&lyp->nfsly_lock);
4878	NFSUNLOCKCLSTATE();
4879}
4880
4881/*
4882 * Search for a devinfo by deviceid. If one is found, return it after
4883 * acquiring a reference count on it.
4884 */
4885struct nfscldevinfo *
4886nfscl_getdevinfo(struct nfsclclient *clp, uint8_t *deviceid,
4887    struct nfscldevinfo *dip)
4888{
4889
4890	NFSLOCKCLSTATE();
4891	if (dip == NULL)
4892		dip = nfscl_finddevinfo(clp, deviceid);
4893	if (dip != NULL)
4894		dip->nfsdi_refcnt++;
4895	NFSUNLOCKCLSTATE();
4896	return (dip);
4897}
4898
4899/*
4900 * Dereference a devinfo structure.
4901 */
4902static void
4903nfscl_reldevinfo_locked(struct nfscldevinfo *dip)
4904{
4905
4906	dip->nfsdi_refcnt--;
4907	if (dip->nfsdi_refcnt == 0)
4908		wakeup(&dip->nfsdi_refcnt);
4909}
4910
4911/*
4912 * Dereference a devinfo structure.
4913 */
4914void
4915nfscl_reldevinfo(struct nfscldevinfo *dip)
4916{
4917
4918	NFSLOCKCLSTATE();
4919	nfscl_reldevinfo_locked(dip);
4920	NFSUNLOCKCLSTATE();
4921}
4922
4923/*
4924 * Find a layout for this file handle. Return NULL upon failure.
4925 */
4926static struct nfscllayout *
4927nfscl_findlayout(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
4928{
4929	struct nfscllayout *lyp;
4930
4931	LIST_FOREACH(lyp, NFSCLLAYOUTHASH(clp, fhp, fhlen), nfsly_hash)
4932		if (lyp->nfsly_fhlen == fhlen &&
4933		    !NFSBCMP(lyp->nfsly_fh, fhp, fhlen))
4934			break;
4935	return (lyp);
4936}
4937
4938/*
4939 * Find a devinfo for this deviceid. Return NULL upon failure.
4940 */
4941static struct nfscldevinfo *
4942nfscl_finddevinfo(struct nfsclclient *clp, uint8_t *deviceid)
4943{
4944	struct nfscldevinfo *dip;
4945
4946	LIST_FOREACH(dip, &clp->nfsc_devinfo, nfsdi_list)
4947		if (NFSBCMP(dip->nfsdi_deviceid, deviceid, NFSX_V4DEVICEID)
4948		    == 0)
4949			break;
4950	return (dip);
4951}
4952
4953/*
4954 * Merge the new file layout list into the main one, maintaining it in
4955 * increasing offset order.
4956 */
4957static void
4958nfscl_mergeflayouts(struct nfsclflayouthead *fhlp,
4959    struct nfsclflayouthead *newfhlp)
4960{
4961	struct nfsclflayout *flp, *nflp, *prevflp, *tflp;
4962
4963	flp = LIST_FIRST(fhlp);
4964	prevflp = NULL;
4965	LIST_FOREACH_SAFE(nflp, newfhlp, nfsfl_list, tflp) {
4966		while (flp != NULL && flp->nfsfl_off < nflp->nfsfl_off) {
4967			prevflp = flp;
4968			flp = LIST_NEXT(flp, nfsfl_list);
4969		}
4970		if (prevflp == NULL)
4971			LIST_INSERT_HEAD(fhlp, nflp, nfsfl_list);
4972		else
4973			LIST_INSERT_AFTER(prevflp, nflp, nfsfl_list);
4974		prevflp = nflp;
4975	}
4976}
4977
4978/*
4979 * Add this nfscldevinfo to the client, if it doesn't already exist.
4980 * This function consumes the structure pointed at by dip, if not NULL.
4981 */
4982APPLESTATIC int
4983nfscl_adddevinfo(struct nfsmount *nmp, struct nfscldevinfo *dip,
4984    struct nfsclflayout *flp)
4985{
4986	struct nfsclclient *clp;
4987	struct nfscldevinfo *tdip;
4988
4989	NFSLOCKCLSTATE();
4990	clp = nmp->nm_clp;
4991	if (clp == NULL) {
4992		NFSUNLOCKCLSTATE();
4993		if (dip != NULL)
4994			free(dip, M_NFSDEVINFO);
4995		return (ENODEV);
4996	}
4997	tdip = nfscl_finddevinfo(clp, flp->nfsfl_dev);
4998	if (tdip != NULL) {
4999		tdip->nfsdi_layoutrefs++;
5000		flp->nfsfl_devp = tdip;
5001		nfscl_reldevinfo_locked(tdip);
5002		NFSUNLOCKCLSTATE();
5003		if (dip != NULL)
5004			free(dip, M_NFSDEVINFO);
5005		return (0);
5006	}
5007	if (dip != NULL) {
5008		LIST_INSERT_HEAD(&clp->nfsc_devinfo, dip, nfsdi_list);
5009		dip->nfsdi_layoutrefs = 1;
5010		flp->nfsfl_devp = dip;
5011	}
5012	NFSUNLOCKCLSTATE();
5013	if (dip == NULL)
5014		return (ENODEV);
5015	return (0);
5016}
5017
5018/*
5019 * Free up a layout structure and associated file layout structure(s).
5020 */
5021APPLESTATIC void
5022nfscl_freelayout(struct nfscllayout *layp)
5023{
5024	struct nfsclflayout *flp, *nflp;
5025	struct nfsclrecalllayout *rp, *nrp;
5026
5027	LIST_FOREACH_SAFE(flp, &layp->nfsly_flayread, nfsfl_list, nflp) {
5028		LIST_REMOVE(flp, nfsfl_list);
5029		nfscl_freeflayout(flp);
5030	}
5031	LIST_FOREACH_SAFE(flp, &layp->nfsly_flayrw, nfsfl_list, nflp) {
5032		LIST_REMOVE(flp, nfsfl_list);
5033		nfscl_freeflayout(flp);
5034	}
5035	LIST_FOREACH_SAFE(rp, &layp->nfsly_recall, nfsrecly_list, nrp) {
5036		LIST_REMOVE(rp, nfsrecly_list);
5037		free(rp, M_NFSLAYRECALL);
5038	}
5039	nfscl_layoutcnt--;
5040	free(layp, M_NFSLAYOUT);
5041}
5042
5043/*
5044 * Free up a file layout structure.
5045 */
5046APPLESTATIC void
5047nfscl_freeflayout(struct nfsclflayout *flp)
5048{
5049	int i;
5050
5051	for (i = 0; i < flp->nfsfl_fhcnt; i++)
5052		free(flp->nfsfl_fh[i], M_NFSFH);
5053	if (flp->nfsfl_devp != NULL)
5054		flp->nfsfl_devp->nfsdi_layoutrefs--;
5055	free(flp, M_NFSFLAYOUT);
5056}
5057
5058/*
5059 * Free up a file layout devinfo structure.
5060 */
5061APPLESTATIC void
5062nfscl_freedevinfo(struct nfscldevinfo *dip)
5063{
5064
5065	free(dip, M_NFSDEVINFO);
5066}
5067
5068/*
5069 * Mark any layouts that match as recalled.
5070 */
5071static int
5072nfscl_layoutrecall(int recalltype, struct nfscllayout *lyp, uint32_t iomode,
5073    uint64_t off, uint64_t len, uint32_t stateseqid,
5074    struct nfsclrecalllayout *recallp)
5075{
5076	struct nfsclrecalllayout *rp, *orp;
5077
5078	recallp->nfsrecly_recalltype = recalltype;
5079	recallp->nfsrecly_iomode = iomode;
5080	recallp->nfsrecly_stateseqid = stateseqid;
5081	recallp->nfsrecly_off = off;
5082	recallp->nfsrecly_len = len;
5083	/*
5084	 * Order the list as file returns first, followed by fsid and any
5085	 * returns, both in increasing stateseqid order.
5086	 * Note that the seqids wrap around, so 1 is after 0xffffffff.
5087	 * (I'm not sure this is correct because I find RFC5661 confusing
5088	 *  on this, but hopefully it will work ok.)
5089	 */
5090	orp = NULL;
5091	LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5092		orp = rp;
5093		if ((recalltype == NFSLAYOUTRETURN_FILE &&
5094		     (rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE ||
5095		      nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) ||
5096		    (recalltype != NFSLAYOUTRETURN_FILE &&
5097		     rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE &&
5098		     nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) {
5099			LIST_INSERT_BEFORE(rp, recallp, nfsrecly_list);
5100			break;
5101		}
5102	}
5103	if (rp == NULL) {
5104		if (orp == NULL)
5105			LIST_INSERT_HEAD(&lyp->nfsly_recall, recallp,
5106			    nfsrecly_list);
5107		else
5108			LIST_INSERT_AFTER(orp, recallp, nfsrecly_list);
5109	}
5110	lyp->nfsly_flags |= NFSLY_RECALL;
5111	return (0);
5112}
5113
5114/*
5115 * Compare the two seqids for ordering. The trick is that the seqids can
5116 * wrap around from 0xffffffff->0, so check for the cases where one
5117 * has wrapped around.
5118 * Return 1 if seqid1 comes before seqid2, 0 otherwise.
5119 */
5120static int
5121nfscl_seq(uint32_t seqid1, uint32_t seqid2)
5122{
5123
5124	if (seqid2 > seqid1 && (seqid2 - seqid1) >= 0x7fffffff)
5125		/* seqid2 has wrapped around. */
5126		return (0);
5127	if (seqid1 > seqid2 && (seqid1 - seqid2) >= 0x7fffffff)
5128		/* seqid1 has wrapped around. */
5129		return (1);
5130	if (seqid1 <= seqid2)
5131		return (1);
5132	return (0);
5133}
5134
5135/*
5136 * Do a layout return for each of the recalls.
5137 */
5138static void
5139nfscl_layoutreturn(struct nfsmount *nmp, struct nfscllayout *lyp,
5140    struct ucred *cred, NFSPROC_T *p)
5141{
5142	struct nfsclrecalllayout *rp;
5143	nfsv4stateid_t stateid;
5144
5145	NFSBCOPY(lyp->nfsly_stateid.other, stateid.other, NFSX_STATEIDOTHER);
5146	LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5147		stateid.seqid = rp->nfsrecly_stateseqid;
5148		(void)nfsrpc_layoutreturn(nmp, lyp->nfsly_fh,
5149		    lyp->nfsly_fhlen, 0, NFSLAYOUT_NFSV4_1_FILES,
5150		    rp->nfsrecly_iomode, rp->nfsrecly_recalltype,
5151		    rp->nfsrecly_off, rp->nfsrecly_len,
5152		    &stateid, 0, NULL, cred, p, NULL);
5153	}
5154}
5155
5156/*
5157 * Do the layout commit for a file layout.
5158 */
5159static void
5160nfscl_dolayoutcommit(struct nfsmount *nmp, struct nfscllayout *lyp,
5161    struct ucred *cred, NFSPROC_T *p)
5162{
5163	struct nfsclflayout *flp;
5164	uint64_t len;
5165	int error;
5166
5167	LIST_FOREACH(flp, &lyp->nfsly_flayrw, nfsfl_list) {
5168		if (flp->nfsfl_off <= lyp->nfsly_lastbyte) {
5169			len = flp->nfsfl_end - flp->nfsfl_off;
5170			error = nfsrpc_layoutcommit(nmp, lyp->nfsly_fh,
5171			    lyp->nfsly_fhlen, 0, flp->nfsfl_off, len,
5172			    lyp->nfsly_lastbyte, &lyp->nfsly_stateid,
5173			    NFSLAYOUT_NFSV4_1_FILES, 0, NULL, cred, p, NULL);
5174			NFSCL_DEBUG(4, "layoutcommit err=%d\n", error);
5175			if (error == NFSERR_NOTSUPP) {
5176				/* If not supported, don't bother doing it. */
5177				NFSLOCKMNT(nmp);
5178				nmp->nm_state |= NFSSTA_NOLAYOUTCOMMIT;
5179				NFSUNLOCKMNT(nmp);
5180				break;
5181			}
5182		}
5183	}
5184}
5185
5186/*
5187 * Commit all layouts for a file (vnode).
5188 */
5189int
5190nfscl_layoutcommit(vnode_t vp, NFSPROC_T *p)
5191{
5192	struct nfsclclient *clp;
5193	struct nfscllayout *lyp;
5194	struct nfsnode *np = VTONFS(vp);
5195	mount_t mp;
5196	struct nfsmount *nmp;
5197
5198	mp = vnode_mount(vp);
5199	nmp = VFSTONFS(mp);
5200	if (NFSHASNOLAYOUTCOMMIT(nmp))
5201		return (0);
5202	NFSLOCKCLSTATE();
5203	clp = nmp->nm_clp;
5204	if (clp == NULL) {
5205		NFSUNLOCKCLSTATE();
5206		return (EPERM);
5207	}
5208	lyp = nfscl_findlayout(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5209	if (lyp == NULL) {
5210		NFSUNLOCKCLSTATE();
5211		return (EPERM);
5212	}
5213	nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
5214	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
5215		NFSUNLOCKCLSTATE();
5216		return (EPERM);
5217	}
5218tryagain:
5219	if ((lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
5220		lyp->nfsly_flags &= ~NFSLY_WRITTEN;
5221		NFSUNLOCKCLSTATE();
5222		NFSCL_DEBUG(4, "do layoutcommit2\n");
5223		nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, NFSPROCCRED(p), p);
5224		NFSLOCKCLSTATE();
5225		goto tryagain;
5226	}
5227	nfsv4_relref(&lyp->nfsly_lock);
5228	NFSUNLOCKCLSTATE();
5229	return (0);
5230}
5231
5232