nfs_clstate.c revision 269398
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 269398 2014-08-01 21:10:41Z 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_recover(clp, cred, p);
2474			} else {
2475				NFSLOCKCLSTATE();
2476				clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2477				NFSUNLOCKCLSTATE();
2478			}
2479		}
2480		if (clp->nfsc_expire <= NFSD_MONOSEC &&
2481		    (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
2482			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
2483			clidrev = clp->nfsc_clientidrev;
2484			error = nfsrpc_renew(clp,
2485			    TAILQ_FIRST(&clp->nfsc_nmp->nm_sess), cred, p);
2486			if (error == NFSERR_CBPATHDOWN)
2487			    cbpathdown = 1;
2488			else if (error == NFSERR_STALECLIENTID ||
2489			    error == NFSERR_BADSESSION) {
2490			    NFSLOCKCLSTATE();
2491			    clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2492			    NFSUNLOCKCLSTATE();
2493			} else if (error == NFSERR_EXPIRED)
2494			    (void) nfscl_hasexpired(clp, clidrev, p);
2495		}
2496
2497		/* Do renews for any DS sessions. */
2498checkdsrenew:
2499		NFSLOCKMNT(clp->nfsc_nmp);
2500		/* Skip first entry, since the MDS is handled above. */
2501		dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess);
2502		if (dsp != NULL)
2503			dsp = TAILQ_NEXT(dsp, nfsclds_list);
2504		while (dsp != NULL) {
2505			if (dsp->nfsclds_expire <= NFSD_MONOSEC) {
2506				dsp->nfsclds_expire = NFSD_MONOSEC +
2507				    clp->nfsc_renew;
2508				NFSUNLOCKMNT(clp->nfsc_nmp);
2509				(void)nfsrpc_renew(clp, dsp, cred, p);
2510				goto checkdsrenew;
2511			}
2512			dsp = TAILQ_NEXT(dsp, nfsclds_list);
2513		}
2514		NFSUNLOCKMNT(clp->nfsc_nmp);
2515
2516		TAILQ_INIT(&dh);
2517		NFSLOCKCLSTATE();
2518		if (cbpathdown)
2519			/* It's a Total Recall! */
2520			nfscl_totalrecall(clp);
2521
2522		/*
2523		 * Now, handle defunct owners.
2524		 */
2525		LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
2526			if (LIST_EMPTY(&owp->nfsow_open)) {
2527				if (owp->nfsow_defunct != 0)
2528					nfscl_freeopenowner(owp, 0);
2529			}
2530		}
2531
2532		/*
2533		 * Do the recall on any delegations. To avoid trouble, always
2534		 * come back up here after having slept.
2535		 */
2536		igotlock = 0;
2537tryagain:
2538		dp = TAILQ_FIRST(&clp->nfsc_deleg);
2539		while (dp != NULL) {
2540			ndp = TAILQ_NEXT(dp, nfsdl_list);
2541			if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
2542				/*
2543				 * Wait for outstanding I/O ops to be done.
2544				 */
2545				if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
2546				    if (igotlock) {
2547					nfsv4_unlock(&clp->nfsc_lock, 0);
2548					igotlock = 0;
2549				    }
2550				    dp->nfsdl_rwlock.nfslock_lock |=
2551					NFSV4LOCK_WANTED;
2552				    (void) nfsmsleep(&dp->nfsdl_rwlock,
2553					NFSCLSTATEMUTEXPTR, PZERO, "nfscld",
2554					NULL);
2555				    goto tryagain;
2556				}
2557				while (!igotlock) {
2558				    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
2559					&islept, NFSCLSTATEMUTEXPTR, NULL);
2560				    if (islept)
2561					goto tryagain;
2562				}
2563				NFSUNLOCKCLSTATE();
2564				newnfs_copycred(&dp->nfsdl_cred, cred);
2565				ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
2566				    NULL, cred, p, 1);
2567				if (!ret) {
2568				    nfscl_cleandeleg(dp);
2569				    TAILQ_REMOVE(&clp->nfsc_deleg, dp,
2570					nfsdl_list);
2571				    LIST_REMOVE(dp, nfsdl_hash);
2572				    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2573				    nfscl_delegcnt--;
2574				    newnfsstats.cldelegates--;
2575				}
2576				NFSLOCKCLSTATE();
2577			}
2578			dp = ndp;
2579		}
2580
2581		/*
2582		 * Clear out old delegations, if we are above the high water
2583		 * mark. Only clear out ones with no state related to them.
2584		 * The tailq list is in LRU order.
2585		 */
2586		dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
2587		while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) {
2588		    ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
2589		    if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
2590			dp->nfsdl_rwlock.nfslock_lock == 0 &&
2591			dp->nfsdl_timestamp < NFSD_MONOSEC &&
2592			(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
2593			  NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) {
2594			clearok = 1;
2595			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2596			    op = LIST_FIRST(&owp->nfsow_open);
2597			    if (op != NULL) {
2598				clearok = 0;
2599				break;
2600			    }
2601			}
2602			if (clearok) {
2603			    LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
2604				if (!LIST_EMPTY(&lp->nfsl_lock)) {
2605				    clearok = 0;
2606				    break;
2607				}
2608			    }
2609			}
2610			if (clearok) {
2611			    TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
2612			    LIST_REMOVE(dp, nfsdl_hash);
2613			    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2614			    nfscl_delegcnt--;
2615			    newnfsstats.cldelegates--;
2616			}
2617		    }
2618		    dp = ndp;
2619		}
2620		if (igotlock)
2621			nfsv4_unlock(&clp->nfsc_lock, 0);
2622
2623		/*
2624		 * Do the recall on any layouts. To avoid trouble, always
2625		 * come back up here after having slept.
2626		 */
2627		TAILQ_INIT(&rlh);
2628tryagain2:
2629		TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) {
2630			if ((lyp->nfsly_flags & NFSLY_RECALL) != 0) {
2631				/*
2632				 * Wait for outstanding I/O ops to be done.
2633				 */
2634				if (lyp->nfsly_lock.nfslock_usecnt > 0 ||
2635				    (lyp->nfsly_lock.nfslock_lock &
2636				     NFSV4LOCK_LOCK) != 0) {
2637					lyp->nfsly_lock.nfslock_lock |=
2638					    NFSV4LOCK_WANTED;
2639					(void)nfsmsleep(&lyp->nfsly_lock,
2640					    NFSCLSTATEMUTEXPTR, PZERO, "nfslyp",
2641					    NULL);
2642					goto tryagain2;
2643				}
2644				/* Move the layout to the recall list. */
2645				TAILQ_REMOVE(&clp->nfsc_layout, lyp,
2646				    nfsly_list);
2647				LIST_REMOVE(lyp, nfsly_hash);
2648				TAILQ_INSERT_HEAD(&rlh, lyp, nfsly_list);
2649
2650				/* Handle any layout commits. */
2651				if (!NFSHASNOLAYOUTCOMMIT(clp->nfsc_nmp) &&
2652				    (lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
2653					lyp->nfsly_flags &= ~NFSLY_WRITTEN;
2654					NFSUNLOCKCLSTATE();
2655					NFSCL_DEBUG(3, "do layoutcommit\n");
2656					nfscl_dolayoutcommit(clp->nfsc_nmp, lyp,
2657					    cred, p);
2658					NFSLOCKCLSTATE();
2659					goto tryagain2;
2660				}
2661			}
2662		}
2663
2664		/* Now, look for stale layouts. */
2665		lyp = TAILQ_LAST(&clp->nfsc_layout, nfscllayouthead);
2666		while (lyp != NULL) {
2667			nlyp = TAILQ_PREV(lyp, nfscllayouthead, nfsly_list);
2668			if (lyp->nfsly_timestamp < NFSD_MONOSEC &&
2669			    (lyp->nfsly_flags & NFSLY_RECALL) == 0 &&
2670			    lyp->nfsly_lock.nfslock_usecnt == 0 &&
2671			    lyp->nfsly_lock.nfslock_lock == 0) {
2672				NFSCL_DEBUG(4, "ret stale lay=%d\n",
2673				    nfscl_layoutcnt);
2674				recallp = malloc(sizeof(*recallp),
2675				    M_NFSLAYRECALL, M_NOWAIT);
2676				if (recallp == NULL)
2677					break;
2678				(void)nfscl_layoutrecall(NFSLAYOUTRETURN_FILE,
2679				    lyp, NFSLAYOUTIOMODE_ANY, 0, UINT64_MAX,
2680				    lyp->nfsly_stateid.seqid, recallp);
2681			}
2682			lyp = nlyp;
2683		}
2684
2685		/*
2686		 * Free up any unreferenced device info structures.
2687		 */
2688		LIST_FOREACH_SAFE(dip, &clp->nfsc_devinfo, nfsdi_list, ndip) {
2689			if (dip->nfsdi_layoutrefs == 0 &&
2690			    dip->nfsdi_refcnt == 0) {
2691				NFSCL_DEBUG(4, "freeing devinfo\n");
2692				LIST_REMOVE(dip, nfsdi_list);
2693				nfscl_freedevinfo(dip);
2694			}
2695		}
2696		NFSUNLOCKCLSTATE();
2697
2698		/* Do layout return(s), as required. */
2699		TAILQ_FOREACH_SAFE(lyp, &rlh, nfsly_list, nlyp) {
2700			TAILQ_REMOVE(&rlh, lyp, nfsly_list);
2701			NFSCL_DEBUG(4, "ret layout\n");
2702			nfscl_layoutreturn(clp->nfsc_nmp, lyp, cred, p);
2703			nfscl_freelayout(lyp);
2704		}
2705
2706		/*
2707		 * Delegreturn any delegations cleaned out or recalled.
2708		 */
2709		TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
2710			newnfs_copycred(&dp->nfsdl_cred, cred);
2711			(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2712			TAILQ_REMOVE(&dh, dp, nfsdl_list);
2713			FREE((caddr_t)dp, M_NFSCLDELEG);
2714		}
2715
2716		SLIST_INIT(&lfh);
2717		/*
2718		 * Call nfscl_cleanupkext() once per second to check for
2719		 * open/lock owners where the process has exited.
2720		 */
2721		mytime = NFSD_MONOSEC;
2722		if (prevsec != mytime) {
2723			prevsec = mytime;
2724			nfscl_cleanupkext(clp, &lfh);
2725		}
2726
2727		/*
2728		 * Do a ReleaseLockOwner for all lock owners where the
2729		 * associated process no longer exists, as found by
2730		 * nfscl_cleanupkext().
2731		 */
2732		newnfs_setroot(cred);
2733		SLIST_FOREACH_SAFE(lfhp, &lfh, nfslfh_list, nlfhp) {
2734			LIST_FOREACH_SAFE(lp, &lfhp->nfslfh_lock, nfsl_list,
2735			    nlp) {
2736				(void)nfsrpc_rellockown(clp->nfsc_nmp, lp,
2737				    lfhp->nfslfh_fh, lfhp->nfslfh_len, cred,
2738				    p);
2739				nfscl_freelockowner(lp, 0);
2740			}
2741			free(lfhp, M_TEMP);
2742		}
2743		SLIST_INIT(&lfh);
2744
2745		NFSLOCKCLSTATE();
2746		if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
2747			(void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl",
2748			    hz);
2749		if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
2750			clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
2751			NFSUNLOCKCLSTATE();
2752			NFSFREECRED(cred);
2753			wakeup((caddr_t)clp);
2754			return;
2755		}
2756		NFSUNLOCKCLSTATE();
2757	}
2758}
2759
2760/*
2761 * Initiate state recovery. Called when NFSERR_STALECLIENTID,
2762 * NFSERR_STALESTATEID or NFSERR_BADSESSION is received.
2763 */
2764APPLESTATIC void
2765nfscl_initiate_recovery(struct nfsclclient *clp)
2766{
2767
2768	if (clp == NULL)
2769		return;
2770	NFSLOCKCLSTATE();
2771	clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2772	NFSUNLOCKCLSTATE();
2773	wakeup((caddr_t)clp);
2774}
2775
2776/*
2777 * Dump out the state stuff for debugging.
2778 */
2779APPLESTATIC void
2780nfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
2781    int lockowner, int locks)
2782{
2783	struct nfsclclient *clp;
2784	struct nfsclowner *owp;
2785	struct nfsclopen *op;
2786	struct nfscllockowner *lp;
2787	struct nfscllock *lop;
2788	struct nfscldeleg *dp;
2789
2790	clp = nmp->nm_clp;
2791	if (clp == NULL) {
2792		printf("nfscl dumpstate NULL clp\n");
2793		return;
2794	}
2795	NFSLOCKCLSTATE();
2796	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2797	  LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2798	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2799		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2800		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2801		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2802		    owp->nfsow_seqid);
2803	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2804		if (opens)
2805		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2806			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2807			op->nfso_stateid.other[2], op->nfso_opencnt,
2808			op->nfso_fh[12]);
2809		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2810		    if (lockowner)
2811			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2812			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2813			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2814			    lp->nfsl_seqid,
2815			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2816			    lp->nfsl_stateid.other[2]);
2817		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2818			if (locks)
2819#ifdef __FreeBSD__
2820			    printf("lck typ=%d fst=%ju end=%ju\n",
2821				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2822				(intmax_t)lop->nfslo_end);
2823#else
2824			    printf("lck typ=%d fst=%qd end=%qd\n",
2825				lop->nfslo_type, lop->nfslo_first,
2826				lop->nfslo_end);
2827#endif
2828		    }
2829		}
2830	    }
2831	  }
2832	}
2833	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2834	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2835		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2836		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2837		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2838		    owp->nfsow_seqid);
2839	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2840		if (opens)
2841		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2842			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2843			op->nfso_stateid.other[2], op->nfso_opencnt,
2844			op->nfso_fh[12]);
2845		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2846		    if (lockowner)
2847			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2848			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2849			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2850			    lp->nfsl_seqid,
2851			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2852			    lp->nfsl_stateid.other[2]);
2853		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2854			if (locks)
2855#ifdef __FreeBSD__
2856			    printf("lck typ=%d fst=%ju end=%ju\n",
2857				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2858				(intmax_t)lop->nfslo_end);
2859#else
2860			    printf("lck typ=%d fst=%qd end=%qd\n",
2861				lop->nfslo_type, lop->nfslo_first,
2862				lop->nfslo_end);
2863#endif
2864		    }
2865		}
2866	    }
2867	}
2868	NFSUNLOCKCLSTATE();
2869}
2870
2871/*
2872 * Check for duplicate open owners and opens.
2873 * (Only used as a diagnostic aid.)
2874 */
2875APPLESTATIC void
2876nfscl_dupopen(vnode_t vp, int dupopens)
2877{
2878	struct nfsclclient *clp;
2879	struct nfsclowner *owp, *owp2;
2880	struct nfsclopen *op, *op2;
2881	struct nfsfh *nfhp;
2882
2883	clp = VFSTONFS(vnode_mount(vp))->nm_clp;
2884	if (clp == NULL) {
2885		printf("nfscl dupopen NULL clp\n");
2886		return;
2887	}
2888	nfhp = VTONFS(vp)->n_fhp;
2889	NFSLOCKCLSTATE();
2890
2891	/*
2892	 * First, search for duplicate owners.
2893	 * These should never happen!
2894	 */
2895	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2896	    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2897		if (owp != owp2 &&
2898		    !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
2899		    NFSV4CL_LOCKNAMELEN)) {
2900			NFSUNLOCKCLSTATE();
2901			printf("DUP OWNER\n");
2902			nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0);
2903			return;
2904		}
2905	    }
2906	}
2907
2908	/*
2909	 * Now, search for duplicate stateids.
2910	 * These shouldn't happen, either.
2911	 */
2912	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2913	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2914		LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2915		    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2916			if (op != op2 &&
2917			    (op->nfso_stateid.other[0] != 0 ||
2918			     op->nfso_stateid.other[1] != 0 ||
2919			     op->nfso_stateid.other[2] != 0) &&
2920			    op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
2921			    op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
2922			    op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
2923			    NFSUNLOCKCLSTATE();
2924			    printf("DUP STATEID\n");
2925			    nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0,
2926				0);
2927			    return;
2928			}
2929		    }
2930		}
2931	    }
2932	}
2933
2934	/*
2935	 * Now search for duplicate opens.
2936	 * Duplicate opens for the same owner
2937	 * should never occur. Other duplicates are
2938	 * possible and are checked for if "dupopens"
2939	 * is true.
2940	 */
2941	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2942	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2943		if (nfhp->nfh_len == op2->nfso_fhlen &&
2944		    !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
2945		    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2946			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2947			    if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
2948				!NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
2949				(!NFSBCMP(op->nfso_own->nfsow_owner,
2950				 op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
2951				 dupopens)) {
2952				if (!NFSBCMP(op->nfso_own->nfsow_owner,
2953				    op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
2954				    NFSUNLOCKCLSTATE();
2955				    printf("BADDUP OPEN\n");
2956				} else {
2957				    NFSUNLOCKCLSTATE();
2958				    printf("DUP OPEN\n");
2959				}
2960				nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1,
2961				    0, 0);
2962				return;
2963			    }
2964			}
2965		    }
2966		}
2967	    }
2968	}
2969	NFSUNLOCKCLSTATE();
2970}
2971
2972/*
2973 * During close, find an open that needs to be dereferenced and
2974 * dereference it. If there are no more opens for this file,
2975 * log a message to that effect.
2976 * Opens aren't actually Close'd until VOP_INACTIVE() is performed
2977 * on the file's vnode.
2978 * This is the safe way, since it is difficult to identify
2979 * which open the close is for and I/O can be performed after the
2980 * close(2) system call when a file is mmap'd.
2981 * If it returns 0 for success, there will be a referenced
2982 * clp returned via clpp.
2983 */
2984APPLESTATIC int
2985nfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
2986{
2987	struct nfsclclient *clp;
2988	struct nfsclowner *owp;
2989	struct nfsclopen *op;
2990	struct nfscldeleg *dp;
2991	struct nfsfh *nfhp;
2992	int error, notdecr;
2993
2994	error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp);
2995	if (error)
2996		return (error);
2997	*clpp = clp;
2998
2999	nfhp = VTONFS(vp)->n_fhp;
3000	notdecr = 1;
3001	NFSLOCKCLSTATE();
3002	/*
3003	 * First, look for one under a delegation that was locally issued
3004	 * and just decrement the opencnt for it. Since all my Opens against
3005	 * the server are DENY_NONE, I don't see a problem with hanging
3006	 * onto them. (It is much easier to use one of the extant Opens
3007	 * that I already have on the server when a Delegation is recalled
3008	 * than to do fresh Opens.) Someday, I might need to rethink this, but.
3009	 */
3010	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3011	if (dp != NULL) {
3012		LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3013			op = LIST_FIRST(&owp->nfsow_open);
3014			if (op != NULL) {
3015				/*
3016				 * Since a delegation is for a file, there
3017				 * should never be more than one open for
3018				 * each openowner.
3019				 */
3020				if (LIST_NEXT(op, nfso_list) != NULL)
3021					panic("nfscdeleg opens");
3022				if (notdecr && op->nfso_opencnt > 0) {
3023					notdecr = 0;
3024					op->nfso_opencnt--;
3025					break;
3026				}
3027			}
3028		}
3029	}
3030
3031	/* Now process the opens against the server. */
3032	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3033		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3034			if (op->nfso_fhlen == nfhp->nfh_len &&
3035			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3036			    nfhp->nfh_len)) {
3037				/* Found an open, decrement cnt if possible */
3038				if (notdecr && op->nfso_opencnt > 0) {
3039					notdecr = 0;
3040					op->nfso_opencnt--;
3041				}
3042				/*
3043				 * There are more opens, so just return.
3044				 */
3045				if (op->nfso_opencnt > 0) {
3046					NFSUNLOCKCLSTATE();
3047					return (0);
3048				}
3049			}
3050		}
3051	}
3052	NFSUNLOCKCLSTATE();
3053	if (notdecr)
3054		printf("nfscl: never fnd open\n");
3055	return (0);
3056}
3057
3058APPLESTATIC int
3059nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
3060{
3061	struct nfsclclient *clp;
3062	struct nfsclowner *owp, *nowp;
3063	struct nfsclopen *op;
3064	struct nfscldeleg *dp;
3065	struct nfsfh *nfhp;
3066	int error;
3067
3068	error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp);
3069	if (error)
3070		return (error);
3071	*clpp = clp;
3072
3073	nfhp = VTONFS(vp)->n_fhp;
3074	NFSLOCKCLSTATE();
3075	/*
3076	 * First get rid of the local Open structures, which should be no
3077	 * longer in use.
3078	 */
3079	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
3080	if (dp != NULL) {
3081		LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
3082			op = LIST_FIRST(&owp->nfsow_open);
3083			if (op != NULL) {
3084				KASSERT((op->nfso_opencnt == 0),
3085				    ("nfscl: bad open cnt on deleg"));
3086				nfscl_freeopen(op, 1);
3087			}
3088			nfscl_freeopenowner(owp, 1);
3089		}
3090	}
3091
3092	/* Return any layouts marked return on close. */
3093	nfscl_retoncloselayout(clp, nfhp->nfh_fh, nfhp->nfh_len);
3094
3095	/* Now process the opens against the server. */
3096lookformore:
3097	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3098		op = LIST_FIRST(&owp->nfsow_open);
3099		while (op != NULL) {
3100			if (op->nfso_fhlen == nfhp->nfh_len &&
3101			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
3102			    nfhp->nfh_len)) {
3103				/* Found an open, close it. */
3104				KASSERT((op->nfso_opencnt == 0),
3105				    ("nfscl: bad open cnt on server"));
3106				NFSUNLOCKCLSTATE();
3107				nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op,
3108				    p);
3109				NFSLOCKCLSTATE();
3110				goto lookformore;
3111			}
3112			op = LIST_NEXT(op, nfso_list);
3113		}
3114	}
3115	NFSUNLOCKCLSTATE();
3116	return (0);
3117}
3118
3119/*
3120 * Return all delegations on this client.
3121 * (Must be called with client sleep lock.)
3122 */
3123static void
3124nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
3125{
3126	struct nfscldeleg *dp, *ndp;
3127	struct ucred *cred;
3128
3129	cred = newnfs_getcred();
3130	TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
3131		nfscl_cleandeleg(dp);
3132		(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
3133		nfscl_freedeleg(&clp->nfsc_deleg, dp);
3134	}
3135	NFSFREECRED(cred);
3136}
3137
3138/*
3139 * Do a callback RPC.
3140 */
3141APPLESTATIC void
3142nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
3143{
3144	int clist, gotseq_ok, i, j, k, op, rcalls;
3145	u_int32_t *tl;
3146	struct nfsclclient *clp;
3147	struct nfscldeleg *dp = NULL;
3148	int numops, taglen = -1, error = 0, trunc;
3149	u_int32_t minorvers = 0, retops = 0, *retopsp = NULL, *repp, cbident;
3150	u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
3151	vnode_t vp = NULL;
3152	struct nfsnode *np;
3153	struct vattr va;
3154	struct nfsfh *nfhp;
3155	mount_t mp;
3156	nfsattrbit_t attrbits, rattrbits;
3157	nfsv4stateid_t stateid;
3158	uint32_t seqid, slotid = 0, highslot, cachethis;
3159	uint8_t sessionid[NFSX_V4SESSIONID];
3160	struct mbuf *rep;
3161	struct nfscllayout *lyp;
3162	uint64_t filesid[2], len, off;
3163	int changed, gotone, laytype, recalltype;
3164	uint32_t iomode;
3165	struct nfsclrecalllayout *recallp = NULL;
3166
3167	gotseq_ok = 0;
3168	nfsrvd_rephead(nd);
3169	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3170	taglen = fxdr_unsigned(int, *tl);
3171	if (taglen < 0) {
3172		error = EBADRPC;
3173		goto nfsmout;
3174	}
3175	if (taglen <= NFSV4_SMALLSTR)
3176		tagstr = tag;
3177	else
3178		tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
3179	error = nfsrv_mtostr(nd, tagstr, taglen);
3180	if (error) {
3181		if (taglen > NFSV4_SMALLSTR)
3182			free(tagstr, M_TEMP);
3183		taglen = -1;
3184		goto nfsmout;
3185	}
3186	(void) nfsm_strtom(nd, tag, taglen);
3187	if (taglen > NFSV4_SMALLSTR) {
3188		free(tagstr, M_TEMP);
3189	}
3190	NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
3191	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3192	minorvers = fxdr_unsigned(u_int32_t, *tl++);
3193	if (minorvers != NFSV4_MINORVERSION && minorvers != NFSV41_MINORVERSION)
3194		nd->nd_repstat = NFSERR_MINORVERMISMATCH;
3195	cbident = fxdr_unsigned(u_int32_t, *tl++);
3196	if (nd->nd_repstat)
3197		numops = 0;
3198	else
3199		numops = fxdr_unsigned(int, *tl);
3200	/*
3201	 * Loop around doing the sub ops.
3202	 */
3203	for (i = 0; i < numops; i++) {
3204		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3205		NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
3206		*repp++ = *tl;
3207		op = fxdr_unsigned(int, *tl);
3208		if (op < NFSV4OP_CBGETATTR ||
3209		   (op > NFSV4OP_CBRECALL && minorvers == NFSV4_MINORVERSION) ||
3210		   (op > NFSV4OP_CBNOTIFYDEVID &&
3211		    minorvers == NFSV41_MINORVERSION)) {
3212		    nd->nd_repstat = NFSERR_OPILLEGAL;
3213		    *repp = nfscl_errmap(nd, minorvers);
3214		    retops++;
3215		    break;
3216		}
3217		nd->nd_procnum = op;
3218		if (op < NFSV4OP_CBNOPS)
3219			newnfsstats.cbrpccnt[nd->nd_procnum]++;
3220		switch (op) {
3221		case NFSV4OP_CBGETATTR:
3222			NFSCL_DEBUG(4, "cbgetattr\n");
3223			mp = NULL;
3224			vp = NULL;
3225			error = nfsm_getfh(nd, &nfhp);
3226			if (!error)
3227				error = nfsrv_getattrbits(nd, &attrbits,
3228				    NULL, NULL);
3229			if (error == 0 && i == 0 &&
3230			    minorvers != NFSV4_MINORVERSION)
3231				error = NFSERR_OPNOTINSESS;
3232			if (!error) {
3233				mp = nfscl_getmnt(minorvers, sessionid, cbident,
3234				    &clp);
3235				if (mp == NULL)
3236					error = NFSERR_SERVERFAULT;
3237			}
3238			if (!error) {
3239				error = nfscl_ngetreopen(mp, nfhp->nfh_fh,
3240				    nfhp->nfh_len, p, &np);
3241				if (!error)
3242					vp = NFSTOV(np);
3243			}
3244			if (!error) {
3245				NFSZERO_ATTRBIT(&rattrbits);
3246				NFSLOCKCLSTATE();
3247				dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3248				    nfhp->nfh_len);
3249				if (dp != NULL) {
3250					if (NFSISSET_ATTRBIT(&attrbits,
3251					    NFSATTRBIT_SIZE)) {
3252						if (vp != NULL)
3253							va.va_size = np->n_size;
3254						else
3255							va.va_size =
3256							    dp->nfsdl_size;
3257						NFSSETBIT_ATTRBIT(&rattrbits,
3258						    NFSATTRBIT_SIZE);
3259					}
3260					if (NFSISSET_ATTRBIT(&attrbits,
3261					    NFSATTRBIT_CHANGE)) {
3262						va.va_filerev =
3263						    dp->nfsdl_change;
3264						if (vp == NULL ||
3265						    (np->n_flag & NDELEGMOD))
3266							va.va_filerev++;
3267						NFSSETBIT_ATTRBIT(&rattrbits,
3268						    NFSATTRBIT_CHANGE);
3269					}
3270				} else
3271					error = NFSERR_SERVERFAULT;
3272				NFSUNLOCKCLSTATE();
3273			}
3274			if (vp != NULL)
3275				vrele(vp);
3276			if (mp != NULL)
3277				vfs_unbusy(mp);
3278			if (nfhp != NULL)
3279				FREE((caddr_t)nfhp, M_NFSFH);
3280			if (!error)
3281				(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
3282				    NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0,
3283				    (uint64_t)0);
3284			break;
3285		case NFSV4OP_CBRECALL:
3286			NFSCL_DEBUG(4, "cbrecall\n");
3287			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
3288			    NFSX_UNSIGNED);
3289			stateid.seqid = *tl++;
3290			NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
3291			    NFSX_STATEIDOTHER);
3292			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3293			trunc = fxdr_unsigned(int, *tl);
3294			error = nfsm_getfh(nd, &nfhp);
3295			if (error == 0 && i == 0 &&
3296			    minorvers != NFSV4_MINORVERSION)
3297				error = NFSERR_OPNOTINSESS;
3298			if (!error) {
3299				NFSLOCKCLSTATE();
3300				if (minorvers == NFSV4_MINORVERSION)
3301					clp = nfscl_getclnt(cbident);
3302				else
3303					clp = nfscl_getclntsess(sessionid);
3304				if (clp != NULL) {
3305					dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3306					    nfhp->nfh_len);
3307					if (dp != NULL && (dp->nfsdl_flags &
3308					    NFSCLDL_DELEGRET) == 0) {
3309						dp->nfsdl_flags |=
3310						    NFSCLDL_RECALL;
3311						wakeup((caddr_t)clp);
3312					}
3313				} else {
3314					error = NFSERR_SERVERFAULT;
3315				}
3316				NFSUNLOCKCLSTATE();
3317			}
3318			if (nfhp != NULL)
3319				FREE((caddr_t)nfhp, M_NFSFH);
3320			break;
3321		case NFSV4OP_CBLAYOUTRECALL:
3322			NFSCL_DEBUG(4, "cblayrec\n");
3323			nfhp = NULL;
3324			NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
3325			laytype = fxdr_unsigned(int, *tl++);
3326			iomode = fxdr_unsigned(uint32_t, *tl++);
3327			if (newnfs_true == *tl++)
3328				changed = 1;
3329			else
3330				changed = 0;
3331			recalltype = fxdr_unsigned(int, *tl);
3332			recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL,
3333			    M_WAITOK);
3334			if (laytype != NFSLAYOUT_NFSV4_1_FILES)
3335				error = NFSERR_NOMATCHLAYOUT;
3336			else if (recalltype == NFSLAYOUTRETURN_FILE) {
3337				error = nfsm_getfh(nd, &nfhp);
3338				NFSCL_DEBUG(4, "retfile getfh=%d\n", error);
3339				if (error != 0)
3340					goto nfsmout;
3341				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER +
3342				    NFSX_STATEID);
3343				off = fxdr_hyper(tl); tl += 2;
3344				len = fxdr_hyper(tl); tl += 2;
3345				stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
3346				NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
3347				if (minorvers == NFSV4_MINORVERSION)
3348					error = NFSERR_NOTSUPP;
3349				else if (i == 0)
3350					error = NFSERR_OPNOTINSESS;
3351				if (error == 0) {
3352					NFSLOCKCLSTATE();
3353					clp = nfscl_getclntsess(sessionid);
3354					NFSCL_DEBUG(4, "cbly clp=%p\n", clp);
3355					if (clp != NULL) {
3356						lyp = nfscl_findlayout(clp,
3357						    nfhp->nfh_fh,
3358						    nfhp->nfh_len);
3359						NFSCL_DEBUG(4, "cblyp=%p\n",
3360						    lyp);
3361						if (lyp != NULL &&
3362						    (lyp->nfsly_flags &
3363						     NFSLY_FILES) != 0 &&
3364						    !NFSBCMP(stateid.other,
3365						    lyp->nfsly_stateid.other,
3366						    NFSX_STATEIDOTHER)) {
3367							error =
3368							    nfscl_layoutrecall(
3369							    recalltype,
3370							    lyp, iomode, off,
3371							    len, stateid.seqid,
3372							    recallp);
3373							recallp = NULL;
3374							wakeup(clp);
3375							NFSCL_DEBUG(4,
3376							    "aft layrcal=%d\n",
3377							    error);
3378						} else
3379							error =
3380							  NFSERR_NOMATCHLAYOUT;
3381					} else
3382						error = NFSERR_NOMATCHLAYOUT;
3383					NFSUNLOCKCLSTATE();
3384				}
3385				free(nfhp, M_NFSFH);
3386			} else if (recalltype == NFSLAYOUTRETURN_FSID) {
3387				NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER);
3388				filesid[0] = fxdr_hyper(tl); tl += 2;
3389				filesid[1] = fxdr_hyper(tl); tl += 2;
3390				gotone = 0;
3391				NFSLOCKCLSTATE();
3392				clp = nfscl_getclntsess(sessionid);
3393				if (clp != NULL) {
3394					TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3395					    nfsly_list) {
3396						if (lyp->nfsly_filesid[0] ==
3397						    filesid[0] &&
3398						    lyp->nfsly_filesid[1] ==
3399						    filesid[1]) {
3400							error =
3401							    nfscl_layoutrecall(
3402							    recalltype,
3403							    lyp, iomode, 0,
3404							    UINT64_MAX,
3405							    lyp->nfsly_stateid.seqid,
3406							    recallp);
3407							recallp = NULL;
3408							gotone = 1;
3409						}
3410					}
3411					if (gotone != 0)
3412						wakeup(clp);
3413					else
3414						error = NFSERR_NOMATCHLAYOUT;
3415				} else
3416					error = NFSERR_NOMATCHLAYOUT;
3417				NFSUNLOCKCLSTATE();
3418			} else if (recalltype == NFSLAYOUTRETURN_ALL) {
3419				gotone = 0;
3420				NFSLOCKCLSTATE();
3421				clp = nfscl_getclntsess(sessionid);
3422				if (clp != NULL) {
3423					TAILQ_FOREACH(lyp, &clp->nfsc_layout,
3424					    nfsly_list) {
3425						error = nfscl_layoutrecall(
3426						    recalltype, lyp, iomode, 0,
3427						    UINT64_MAX,
3428						    lyp->nfsly_stateid.seqid,
3429						    recallp);
3430						recallp = NULL;
3431						gotone = 1;
3432					}
3433					if (gotone != 0)
3434						wakeup(clp);
3435					else
3436						error = NFSERR_NOMATCHLAYOUT;
3437				} else
3438					error = NFSERR_NOMATCHLAYOUT;
3439				NFSUNLOCKCLSTATE();
3440			} else
3441				error = NFSERR_NOMATCHLAYOUT;
3442			if (recallp != NULL) {
3443				free(recallp, M_NFSLAYRECALL);
3444				recallp = NULL;
3445			}
3446			break;
3447		case NFSV4OP_CBSEQUENCE:
3448			NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3449			    5 * NFSX_UNSIGNED);
3450			bcopy(tl, sessionid, NFSX_V4SESSIONID);
3451			tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3452			seqid = fxdr_unsigned(uint32_t, *tl++);
3453			slotid = fxdr_unsigned(uint32_t, *tl++);
3454			highslot = fxdr_unsigned(uint32_t, *tl++);
3455			cachethis = *tl++;
3456			/* Throw away the referring call stuff. */
3457			clist = fxdr_unsigned(int, *tl);
3458			for (j = 0; j < clist; j++) {
3459				NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
3460				    NFSX_UNSIGNED);
3461				tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3462				rcalls = fxdr_unsigned(int, *tl);
3463				for (k = 0; k < rcalls; k++) {
3464					NFSM_DISSECT(tl, uint32_t *,
3465					    2 * NFSX_UNSIGNED);
3466				}
3467			}
3468			NFSLOCKCLSTATE();
3469			if (i == 0) {
3470				clp = nfscl_getclntsess(sessionid);
3471				if (clp == NULL)
3472					error = NFSERR_SERVERFAULT;
3473			} else
3474				error = NFSERR_SEQUENCEPOS;
3475			if (error == 0)
3476				error = nfsv4_seqsession(seqid, slotid,
3477				    highslot,
3478				    NFSMNT_MDSSESSION(clp->nfsc_nmp)->
3479				    nfsess_cbslots, &rep,
3480				    NFSMNT_MDSSESSION(clp->nfsc_nmp)->
3481				    nfsess_backslots);
3482			NFSUNLOCKCLSTATE();
3483			if (error == 0) {
3484				gotseq_ok = 1;
3485				if (rep != NULL) {
3486					NFSCL_DEBUG(4, "Got cbretry\n");
3487					m_freem(nd->nd_mreq);
3488					nd->nd_mreq = rep;
3489					rep = NULL;
3490					goto out;
3491				}
3492				NFSM_BUILD(tl, uint32_t *,
3493				    NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
3494				bcopy(sessionid, tl, NFSX_V4SESSIONID);
3495				tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3496				*tl++ = txdr_unsigned(seqid);
3497				*tl++ = txdr_unsigned(slotid);
3498				*tl++ = txdr_unsigned(NFSV4_CBSLOTS - 1);
3499				*tl = txdr_unsigned(NFSV4_CBSLOTS - 1);
3500			}
3501			break;
3502		default:
3503			if (i == 0 && minorvers == NFSV41_MINORVERSION)
3504				error = NFSERR_OPNOTINSESS;
3505			else {
3506				NFSCL_DEBUG(1, "unsupp callback %d\n", op);
3507				error = NFSERR_NOTSUPP;
3508			}
3509			break;
3510		};
3511		if (error) {
3512			if (error == EBADRPC || error == NFSERR_BADXDR) {
3513				nd->nd_repstat = NFSERR_BADXDR;
3514			} else {
3515				nd->nd_repstat = error;
3516			}
3517			error = 0;
3518		}
3519		retops++;
3520		if (nd->nd_repstat) {
3521			*repp = nfscl_errmap(nd, minorvers);
3522			break;
3523		} else
3524			*repp = 0;	/* NFS4_OK */
3525	}
3526nfsmout:
3527	if (recallp != NULL)
3528		free(recallp, M_NFSLAYRECALL);
3529	if (error) {
3530		if (error == EBADRPC || error == NFSERR_BADXDR)
3531			nd->nd_repstat = NFSERR_BADXDR;
3532		else
3533			printf("nfsv4 comperr1=%d\n", error);
3534	}
3535	if (taglen == -1) {
3536		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3537		*tl++ = 0;
3538		*tl = 0;
3539	} else {
3540		*retopsp = txdr_unsigned(retops);
3541	}
3542	*nd->nd_errp = nfscl_errmap(nd, minorvers);
3543out:
3544	if (gotseq_ok != 0) {
3545		rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK);
3546		NFSLOCKCLSTATE();
3547		clp = nfscl_getclntsess(sessionid);
3548		if (clp != NULL) {
3549			nfsv4_seqsess_cacherep(slotid,
3550			    NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_cbslots,
3551			    NFSERR_OK, &rep);
3552			NFSUNLOCKCLSTATE();
3553		} else {
3554			NFSUNLOCKCLSTATE();
3555			m_freem(rep);
3556		}
3557	}
3558}
3559
3560/*
3561 * Generate the next cbident value. Basically just increment a static value
3562 * and then check that it isn't already in the list, if it has wrapped around.
3563 */
3564static u_int32_t
3565nfscl_nextcbident(void)
3566{
3567	struct nfsclclient *clp;
3568	int matched;
3569	static u_int32_t nextcbident = 0;
3570	static int haswrapped = 0;
3571
3572	nextcbident++;
3573	if (nextcbident == 0)
3574		haswrapped = 1;
3575	if (haswrapped) {
3576		/*
3577		 * Search the clientid list for one already using this cbident.
3578		 */
3579		do {
3580			matched = 0;
3581			NFSLOCKCLSTATE();
3582			LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3583				if (clp->nfsc_cbident == nextcbident) {
3584					matched = 1;
3585					break;
3586				}
3587			}
3588			NFSUNLOCKCLSTATE();
3589			if (matched == 1)
3590				nextcbident++;
3591		} while (matched);
3592	}
3593	return (nextcbident);
3594}
3595
3596/*
3597 * Get the mount point related to a given cbident or session and busy it.
3598 */
3599static mount_t
3600nfscl_getmnt(int minorvers, uint8_t *sessionid, u_int32_t cbident,
3601    struct nfsclclient **clpp)
3602{
3603	struct nfsclclient *clp;
3604	mount_t mp;
3605	int error;
3606
3607	*clpp = NULL;
3608	NFSLOCKCLSTATE();
3609	LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3610		if (minorvers == NFSV4_MINORVERSION) {
3611			if (clp->nfsc_cbident == cbident)
3612				break;
3613		} else if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)->
3614		    nfsess_sessionid, sessionid, NFSX_V4SESSIONID))
3615			break;
3616	}
3617	if (clp == NULL) {
3618		NFSUNLOCKCLSTATE();
3619		return (NULL);
3620	}
3621	mp = clp->nfsc_nmp->nm_mountp;
3622	vfs_ref(mp);
3623	NFSUNLOCKCLSTATE();
3624	error = vfs_busy(mp, 0);
3625	vfs_rel(mp);
3626	if (error != 0)
3627		return (NULL);
3628	*clpp = clp;
3629	return (mp);
3630}
3631
3632/*
3633 * Get the clientid pointer related to a given cbident.
3634 */
3635static struct nfsclclient *
3636nfscl_getclnt(u_int32_t cbident)
3637{
3638	struct nfsclclient *clp;
3639
3640	LIST_FOREACH(clp, &nfsclhead, nfsc_list)
3641		if (clp->nfsc_cbident == cbident)
3642			break;
3643	return (clp);
3644}
3645
3646/*
3647 * Get the clientid pointer related to a given sessionid.
3648 */
3649static struct nfsclclient *
3650nfscl_getclntsess(uint8_t *sessionid)
3651{
3652	struct nfsclclient *clp;
3653
3654	LIST_FOREACH(clp, &nfsclhead, nfsc_list)
3655		if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_sessionid,
3656		    sessionid, NFSX_V4SESSIONID))
3657			break;
3658	return (clp);
3659}
3660
3661/*
3662 * Search for a lock conflict locally on the client. A conflict occurs if
3663 * - not same owner and overlapping byte range and at least one of them is
3664 *   a write lock or this is an unlock.
3665 */
3666static int
3667nfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
3668    struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
3669    struct nfscllock **lopp)
3670{
3671	struct nfsclowner *owp;
3672	struct nfsclopen *op;
3673	int ret;
3674
3675	if (dp != NULL) {
3676		ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
3677		if (ret)
3678			return (ret);
3679	}
3680	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3681		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3682			if (op->nfso_fhlen == fhlen &&
3683			    !NFSBCMP(op->nfso_fh, fhp, fhlen)) {
3684				ret = nfscl_checkconflict(&op->nfso_lock, nlop,
3685				    own, lopp);
3686				if (ret)
3687					return (ret);
3688			}
3689		}
3690	}
3691	return (0);
3692}
3693
3694static int
3695nfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
3696    u_int8_t *own, struct nfscllock **lopp)
3697{
3698	struct nfscllockowner *lp;
3699	struct nfscllock *lop;
3700
3701	LIST_FOREACH(lp, lhp, nfsl_list) {
3702		if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
3703			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3704				if (lop->nfslo_first >= nlop->nfslo_end)
3705					break;
3706				if (lop->nfslo_end <= nlop->nfslo_first)
3707					continue;
3708				if (lop->nfslo_type == F_WRLCK ||
3709				    nlop->nfslo_type == F_WRLCK ||
3710				    nlop->nfslo_type == F_UNLCK) {
3711					if (lopp != NULL)
3712						*lopp = lop;
3713					return (NFSERR_DENIED);
3714				}
3715			}
3716		}
3717	}
3718	return (0);
3719}
3720
3721/*
3722 * Check for a local conflicting lock.
3723 */
3724APPLESTATIC int
3725nfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
3726    u_int64_t len, struct flock *fl, NFSPROC_T *p, void *id, int flags)
3727{
3728	struct nfscllock *lop, nlck;
3729	struct nfscldeleg *dp;
3730	struct nfsnode *np;
3731	u_int8_t own[NFSV4CL_LOCKNAMELEN];
3732	int error;
3733
3734	nlck.nfslo_type = fl->l_type;
3735	nlck.nfslo_first = off;
3736	if (len == NFS64BITSSET) {
3737		nlck.nfslo_end = NFS64BITSSET;
3738	} else {
3739		nlck.nfslo_end = off + len;
3740		if (nlck.nfslo_end <= nlck.nfslo_first)
3741			return (NFSERR_INVAL);
3742	}
3743	np = VTONFS(vp);
3744	nfscl_filllockowner(id, own, flags);
3745	NFSLOCKCLSTATE();
3746	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3747	error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
3748	    &nlck, own, dp, &lop);
3749	if (error != 0) {
3750		fl->l_whence = SEEK_SET;
3751		fl->l_start = lop->nfslo_first;
3752		if (lop->nfslo_end == NFS64BITSSET)
3753			fl->l_len = 0;
3754		else
3755			fl->l_len = lop->nfslo_end - lop->nfslo_first;
3756		fl->l_pid = (pid_t)0;
3757		fl->l_type = lop->nfslo_type;
3758		error = -1;			/* no RPC required */
3759	} else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
3760	    fl->l_type == F_RDLCK)) {
3761		/*
3762		 * The delegation ensures that there isn't a conflicting
3763		 * lock on the server, so return -1 to indicate an RPC
3764		 * isn't required.
3765		 */
3766		fl->l_type = F_UNLCK;
3767		error = -1;
3768	}
3769	NFSUNLOCKCLSTATE();
3770	return (error);
3771}
3772
3773/*
3774 * Handle Recall of a delegation.
3775 * The clp must be exclusive locked when this is called.
3776 */
3777static int
3778nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
3779    struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p,
3780    int called_from_renewthread)
3781{
3782	struct nfsclowner *owp, *lowp, *nowp;
3783	struct nfsclopen *op, *lop;
3784	struct nfscllockowner *lp;
3785	struct nfscllock *lckp;
3786	struct nfsnode *np;
3787	int error = 0, ret, gotvp = 0;
3788
3789	if (vp == NULL) {
3790		/*
3791		 * First, get a vnode for the file. This is needed to do RPCs.
3792		 */
3793		ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
3794		    dp->nfsdl_fhlen, p, &np);
3795		if (ret) {
3796			/*
3797			 * File isn't open, so nothing to move over to the
3798			 * server.
3799			 */
3800			return (0);
3801		}
3802		vp = NFSTOV(np);
3803		gotvp = 1;
3804	} else {
3805		np = VTONFS(vp);
3806	}
3807	dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
3808
3809	/*
3810	 * Ok, if it's a write delegation, flush data to the server, so
3811	 * that close/open consistency is retained.
3812	 */
3813	ret = 0;
3814	NFSLOCKNODE(np);
3815	if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
3816		np->n_flag |= NDELEGRECALL;
3817		NFSUNLOCKNODE(np);
3818		ret = ncl_flush(vp, MNT_WAIT, cred, p, 1,
3819		    called_from_renewthread);
3820		NFSLOCKNODE(np);
3821		np->n_flag &= ~NDELEGRECALL;
3822	}
3823	NFSINVALATTRCACHE(np);
3824	NFSUNLOCKNODE(np);
3825	if (ret == EIO && called_from_renewthread != 0) {
3826		/*
3827		 * If the flush failed with EIO for the renew thread,
3828		 * return now, so that the dirty buffer will be flushed
3829		 * later.
3830		 */
3831		if (gotvp != 0)
3832			vrele(vp);
3833		return (ret);
3834	}
3835
3836	/*
3837	 * Now, for each openowner with opens issued locally, move them
3838	 * over to state against the server.
3839	 */
3840	LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
3841		lop = LIST_FIRST(&lowp->nfsow_open);
3842		if (lop != NULL) {
3843			if (LIST_NEXT(lop, nfso_list) != NULL)
3844				panic("nfsdlg mult opens");
3845			/*
3846			 * Look for the same openowner against the server.
3847			 */
3848			LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3849				if (!NFSBCMP(lowp->nfsow_owner,
3850				    owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
3851					newnfs_copycred(&dp->nfsdl_cred, cred);
3852					ret = nfscl_moveopen(vp, clp, nmp, lop,
3853					    owp, dp, cred, p);
3854					if (ret == NFSERR_STALECLIENTID ||
3855					    ret == NFSERR_STALEDONTRECOVER ||
3856					    ret == NFSERR_BADSESSION) {
3857						if (gotvp)
3858							vrele(vp);
3859						return (ret);
3860					}
3861					if (ret) {
3862						nfscl_freeopen(lop, 1);
3863						if (!error)
3864							error = ret;
3865					}
3866					break;
3867				}
3868			}
3869
3870			/*
3871			 * If no openowner found, create one and get an open
3872			 * for it.
3873			 */
3874			if (owp == NULL) {
3875				MALLOC(nowp, struct nfsclowner *,
3876				    sizeof (struct nfsclowner), M_NFSCLOWNER,
3877				    M_WAITOK);
3878				nfscl_newopen(clp, NULL, &owp, &nowp, &op,
3879				    NULL, lowp->nfsow_owner, dp->nfsdl_fh,
3880				    dp->nfsdl_fhlen, NULL);
3881				newnfs_copycred(&dp->nfsdl_cred, cred);
3882				ret = nfscl_moveopen(vp, clp, nmp, lop,
3883				    owp, dp, cred, p);
3884				if (ret) {
3885					nfscl_freeopenowner(owp, 0);
3886					if (ret == NFSERR_STALECLIENTID ||
3887					    ret == NFSERR_STALEDONTRECOVER ||
3888					    ret == NFSERR_BADSESSION) {
3889						if (gotvp)
3890							vrele(vp);
3891						return (ret);
3892					}
3893					if (ret) {
3894						nfscl_freeopen(lop, 1);
3895						if (!error)
3896							error = ret;
3897					}
3898				}
3899			}
3900		}
3901	}
3902
3903	/*
3904	 * Now, get byte range locks for any locks done locally.
3905	 */
3906	LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3907		LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
3908			newnfs_copycred(&dp->nfsdl_cred, cred);
3909			ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
3910			if (ret == NFSERR_STALESTATEID ||
3911			    ret == NFSERR_STALEDONTRECOVER ||
3912			    ret == NFSERR_STALECLIENTID ||
3913			    ret == NFSERR_BADSESSION) {
3914				if (gotvp)
3915					vrele(vp);
3916				return (ret);
3917			}
3918			if (ret && !error)
3919				error = ret;
3920		}
3921	}
3922	if (gotvp)
3923		vrele(vp);
3924	return (error);
3925}
3926
3927/*
3928 * Move a locally issued open over to an owner on the state list.
3929 * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
3930 * returns with it unlocked.
3931 */
3932static int
3933nfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3934    struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
3935    struct ucred *cred, NFSPROC_T *p)
3936{
3937	struct nfsclopen *op, *nop;
3938	struct nfscldeleg *ndp;
3939	struct nfsnode *np;
3940	int error = 0, newone;
3941
3942	/*
3943	 * First, look for an appropriate open, If found, just increment the
3944	 * opencnt in it.
3945	 */
3946	LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3947		if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
3948		    op->nfso_fhlen == lop->nfso_fhlen &&
3949		    !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
3950			op->nfso_opencnt += lop->nfso_opencnt;
3951			nfscl_freeopen(lop, 1);
3952			return (0);
3953		}
3954	}
3955
3956	/* No appropriate open, so we have to do one against the server. */
3957	np = VTONFS(vp);
3958	MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
3959	    lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
3960	newone = 0;
3961	nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
3962	    lop->nfso_fh, lop->nfso_fhlen, &newone);
3963	ndp = dp;
3964	error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen,
3965	    lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
3966	    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
3967	if (error) {
3968		if (newone)
3969			nfscl_freeopen(op, 0);
3970	} else {
3971		if (newone)
3972			newnfs_copyincred(cred, &op->nfso_cred);
3973		op->nfso_mode |= lop->nfso_mode;
3974		op->nfso_opencnt += lop->nfso_opencnt;
3975		nfscl_freeopen(lop, 1);
3976	}
3977	if (nop != NULL)
3978		FREE((caddr_t)nop, M_NFSCLOPEN);
3979	if (ndp != NULL) {
3980		/*
3981		 * What should I do with the returned delegation, since the
3982		 * delegation is being recalled? For now, just printf and
3983		 * through it away.
3984		 */
3985		printf("Moveopen returned deleg\n");
3986		FREE((caddr_t)ndp, M_NFSCLDELEG);
3987	}
3988	return (error);
3989}
3990
3991/*
3992 * Recall all delegations on this client.
3993 */
3994static void
3995nfscl_totalrecall(struct nfsclclient *clp)
3996{
3997	struct nfscldeleg *dp;
3998
3999	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
4000		if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0)
4001			dp->nfsdl_flags |= NFSCLDL_RECALL;
4002	}
4003}
4004
4005/*
4006 * Relock byte ranges. Called for delegation recall and state expiry.
4007 */
4008static int
4009nfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
4010    struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
4011    NFSPROC_T *p)
4012{
4013	struct nfscllockowner *nlp;
4014	struct nfsfh *nfhp;
4015	u_int64_t off, len;
4016	u_int32_t clidrev = 0;
4017	int error, newone, donelocally;
4018
4019	off = lop->nfslo_first;
4020	len = lop->nfslo_end - lop->nfslo_first;
4021	error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
4022	    clp, 1, NULL, lp->nfsl_lockflags, lp->nfsl_owner,
4023	    lp->nfsl_openowner, &nlp, &newone, &donelocally);
4024	if (error || donelocally)
4025		return (error);
4026	if (nmp->nm_clp != NULL)
4027		clidrev = nmp->nm_clp->nfsc_clientidrev;
4028	else
4029		clidrev = 0;
4030	nfhp = VTONFS(vp)->n_fhp;
4031	error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
4032	    nfhp->nfh_len, nlp, newone, 0, off,
4033	    len, lop->nfslo_type, cred, p);
4034	if (error)
4035		nfscl_freelockowner(nlp, 0);
4036	return (error);
4037}
4038
4039/*
4040 * Called to re-open a file. Basically get a vnode for the file handle
4041 * and then call nfsrpc_openrpc() to do the rest.
4042 */
4043static int
4044nfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
4045    u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
4046    struct ucred *cred, NFSPROC_T *p)
4047{
4048	struct nfsnode *np;
4049	vnode_t vp;
4050	int error;
4051
4052	error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
4053	if (error)
4054		return (error);
4055	vp = NFSTOV(np);
4056	if (np->n_v4 != NULL) {
4057		error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
4058		    np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
4059		    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
4060		    cred, p);
4061	} else {
4062		error = EINVAL;
4063	}
4064	vrele(vp);
4065	return (error);
4066}
4067
4068/*
4069 * Try an open against the server. Just call nfsrpc_openrpc(), retrying while
4070 * NFSERR_DELAY. Also, try system credentials, if the passed in credentials
4071 * fail.
4072 */
4073static int
4074nfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4075    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
4076    u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
4077    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
4078{
4079	int error;
4080
4081	do {
4082		error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
4083		    mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
4084		    0, 0);
4085		if (error == NFSERR_DELAY)
4086			(void) nfs_catnap(PZERO, error, "nfstryop");
4087	} while (error == NFSERR_DELAY);
4088	if (error == EAUTH || error == EACCES) {
4089		/* Try again using system credentials */
4090		newnfs_setroot(cred);
4091		do {
4092		    error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
4093			newfhlen, mode, op, name, namelen, ndpp, reclaim,
4094			delegtype, cred, p, 1, 0);
4095		    if (error == NFSERR_DELAY)
4096			(void) nfs_catnap(PZERO, error, "nfstryop");
4097		} while (error == NFSERR_DELAY);
4098	}
4099	return (error);
4100}
4101
4102/*
4103 * Try a byte range lock. Just loop on nfsrpc_lock() while it returns
4104 * NFSERR_DELAY. Also, retry with system credentials, if the provided
4105 * cred don't work.
4106 */
4107static int
4108nfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
4109    int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
4110    u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
4111{
4112	struct nfsrv_descript nfsd, *nd = &nfsd;
4113	int error;
4114
4115	do {
4116		error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
4117		    reclaim, off, len, type, cred, p, 0);
4118		if (!error && nd->nd_repstat == NFSERR_DELAY)
4119			(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4120			    "nfstrylck");
4121	} while (!error && nd->nd_repstat == NFSERR_DELAY);
4122	if (!error)
4123		error = nd->nd_repstat;
4124	if (error == EAUTH || error == EACCES) {
4125		/* Try again using root credentials */
4126		newnfs_setroot(cred);
4127		do {
4128			error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
4129			    newone, reclaim, off, len, type, cred, p, 1);
4130			if (!error && nd->nd_repstat == NFSERR_DELAY)
4131				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
4132				    "nfstrylck");
4133		} while (!error && nd->nd_repstat == NFSERR_DELAY);
4134		if (!error)
4135			error = nd->nd_repstat;
4136	}
4137	return (error);
4138}
4139
4140/*
4141 * Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
4142 * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4143 * credentials fail.
4144 */
4145static int
4146nfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
4147    struct nfsmount *nmp, NFSPROC_T *p)
4148{
4149	int error;
4150
4151	do {
4152		error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
4153		if (error == NFSERR_DELAY)
4154			(void) nfs_catnap(PZERO, error, "nfstrydp");
4155	} while (error == NFSERR_DELAY);
4156	if (error == EAUTH || error == EACCES) {
4157		/* Try again using system credentials */
4158		newnfs_setroot(cred);
4159		do {
4160			error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
4161			if (error == NFSERR_DELAY)
4162				(void) nfs_catnap(PZERO, error, "nfstrydp");
4163		} while (error == NFSERR_DELAY);
4164	}
4165	return (error);
4166}
4167
4168/*
4169 * Try a close against the server. Just call nfsrpc_closerpc(),
4170 * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
4171 * credentials fail.
4172 */
4173APPLESTATIC int
4174nfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
4175    struct nfsmount *nmp, NFSPROC_T *p)
4176{
4177	struct nfsrv_descript nfsd, *nd = &nfsd;
4178	int error;
4179
4180	do {
4181		error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
4182		if (error == NFSERR_DELAY)
4183			(void) nfs_catnap(PZERO, error, "nfstrycl");
4184	} while (error == NFSERR_DELAY);
4185	if (error == EAUTH || error == EACCES) {
4186		/* Try again using system credentials */
4187		newnfs_setroot(cred);
4188		do {
4189			error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
4190			if (error == NFSERR_DELAY)
4191				(void) nfs_catnap(PZERO, error, "nfstrycl");
4192		} while (error == NFSERR_DELAY);
4193	}
4194	return (error);
4195}
4196
4197/*
4198 * Decide if a delegation on a file permits close without flushing writes
4199 * to the server. This might be a big performance win in some environments.
4200 * (Not useful until the client does caching on local stable storage.)
4201 */
4202APPLESTATIC int
4203nfscl_mustflush(vnode_t vp)
4204{
4205	struct nfsclclient *clp;
4206	struct nfscldeleg *dp;
4207	struct nfsnode *np;
4208	struct nfsmount *nmp;
4209
4210	np = VTONFS(vp);
4211	nmp = VFSTONFS(vnode_mount(vp));
4212	if (!NFSHASNFSV4(nmp))
4213		return (1);
4214	NFSLOCKCLSTATE();
4215	clp = nfscl_findcl(nmp);
4216	if (clp == NULL) {
4217		NFSUNLOCKCLSTATE();
4218		return (1);
4219	}
4220	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4221	if (dp != NULL && (dp->nfsdl_flags &
4222	    (NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) ==
4223	     NFSCLDL_WRITE &&
4224	    (dp->nfsdl_sizelimit >= np->n_size ||
4225	     !NFSHASSTRICT3530(nmp))) {
4226		NFSUNLOCKCLSTATE();
4227		return (0);
4228	}
4229	NFSUNLOCKCLSTATE();
4230	return (1);
4231}
4232
4233/*
4234 * See if a (write) delegation exists for this file.
4235 */
4236APPLESTATIC int
4237nfscl_nodeleg(vnode_t vp, int writedeleg)
4238{
4239	struct nfsclclient *clp;
4240	struct nfscldeleg *dp;
4241	struct nfsnode *np;
4242	struct nfsmount *nmp;
4243
4244	np = VTONFS(vp);
4245	nmp = VFSTONFS(vnode_mount(vp));
4246	if (!NFSHASNFSV4(nmp))
4247		return (1);
4248	NFSLOCKCLSTATE();
4249	clp = nfscl_findcl(nmp);
4250	if (clp == NULL) {
4251		NFSUNLOCKCLSTATE();
4252		return (1);
4253	}
4254	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4255	if (dp != NULL &&
4256	    (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 &&
4257	    (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) ==
4258	     NFSCLDL_WRITE)) {
4259		NFSUNLOCKCLSTATE();
4260		return (0);
4261	}
4262	NFSUNLOCKCLSTATE();
4263	return (1);
4264}
4265
4266/*
4267 * Look for an associated delegation that should be DelegReturned.
4268 */
4269APPLESTATIC int
4270nfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
4271{
4272	struct nfsclclient *clp;
4273	struct nfscldeleg *dp;
4274	struct nfsclowner *owp;
4275	struct nfscllockowner *lp;
4276	struct nfsmount *nmp;
4277	struct ucred *cred;
4278	struct nfsnode *np;
4279	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4280
4281	nmp = VFSTONFS(vnode_mount(vp));
4282	np = VTONFS(vp);
4283	NFSLOCKCLSTATE();
4284	/*
4285	 * Loop around waiting for:
4286	 * - outstanding I/O operations on delegations to complete
4287	 * - for a delegation on vp that has state, lock the client and
4288	 *   do a recall
4289	 * - return delegation with no state
4290	 */
4291	while (1) {
4292		clp = nfscl_findcl(nmp);
4293		if (clp == NULL) {
4294			NFSUNLOCKCLSTATE();
4295			return (retcnt);
4296		}
4297		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4298		    np->n_fhp->nfh_len);
4299		if (dp != NULL) {
4300		    /*
4301		     * Wait for outstanding I/O ops to be done.
4302		     */
4303		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4304			if (igotlock) {
4305			    nfsv4_unlock(&clp->nfsc_lock, 0);
4306			    igotlock = 0;
4307			}
4308			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4309			(void) nfsmsleep(&dp->nfsdl_rwlock,
4310			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4311			continue;
4312		    }
4313		    needsrecall = 0;
4314		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4315			if (!LIST_EMPTY(&owp->nfsow_open)) {
4316			    needsrecall = 1;
4317			    break;
4318			}
4319		    }
4320		    if (!needsrecall) {
4321			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4322			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4323				needsrecall = 1;
4324				break;
4325			    }
4326			}
4327		    }
4328		    if (needsrecall && !triedrecall) {
4329			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4330			islept = 0;
4331			while (!igotlock) {
4332			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4333				&islept, NFSCLSTATEMUTEXPTR, NULL);
4334			    if (islept)
4335				break;
4336			}
4337			if (islept)
4338			    continue;
4339			NFSUNLOCKCLSTATE();
4340			cred = newnfs_getcred();
4341			newnfs_copycred(&dp->nfsdl_cred, cred);
4342			(void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0);
4343			NFSFREECRED(cred);
4344			triedrecall = 1;
4345			NFSLOCKCLSTATE();
4346			nfsv4_unlock(&clp->nfsc_lock, 0);
4347			igotlock = 0;
4348			continue;
4349		    }
4350		    *stp = dp->nfsdl_stateid;
4351		    retcnt = 1;
4352		    nfscl_cleandeleg(dp);
4353		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4354		}
4355		if (igotlock)
4356		    nfsv4_unlock(&clp->nfsc_lock, 0);
4357		NFSUNLOCKCLSTATE();
4358		return (retcnt);
4359	}
4360}
4361
4362/*
4363 * Look for associated delegation(s) that should be DelegReturned.
4364 */
4365APPLESTATIC int
4366nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
4367    nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
4368{
4369	struct nfsclclient *clp;
4370	struct nfscldeleg *dp;
4371	struct nfsclowner *owp;
4372	struct nfscllockowner *lp;
4373	struct nfsmount *nmp;
4374	struct ucred *cred;
4375	struct nfsnode *np;
4376	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
4377
4378	nmp = VFSTONFS(vnode_mount(fvp));
4379	*gotfdp = 0;
4380	*gottdp = 0;
4381	NFSLOCKCLSTATE();
4382	/*
4383	 * Loop around waiting for:
4384	 * - outstanding I/O operations on delegations to complete
4385	 * - for a delegation on fvp that has state, lock the client and
4386	 *   do a recall
4387	 * - return delegation(s) with no state.
4388	 */
4389	while (1) {
4390		clp = nfscl_findcl(nmp);
4391		if (clp == NULL) {
4392			NFSUNLOCKCLSTATE();
4393			return (retcnt);
4394		}
4395		np = VTONFS(fvp);
4396		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4397		    np->n_fhp->nfh_len);
4398		if (dp != NULL && *gotfdp == 0) {
4399		    /*
4400		     * Wait for outstanding I/O ops to be done.
4401		     */
4402		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4403			if (igotlock) {
4404			    nfsv4_unlock(&clp->nfsc_lock, 0);
4405			    igotlock = 0;
4406			}
4407			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4408			(void) nfsmsleep(&dp->nfsdl_rwlock,
4409			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4410			continue;
4411		    }
4412		    needsrecall = 0;
4413		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4414			if (!LIST_EMPTY(&owp->nfsow_open)) {
4415			    needsrecall = 1;
4416			    break;
4417			}
4418		    }
4419		    if (!needsrecall) {
4420			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4421			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4422				needsrecall = 1;
4423				break;
4424			    }
4425			}
4426		    }
4427		    if (needsrecall && !triedrecall) {
4428			dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4429			islept = 0;
4430			while (!igotlock) {
4431			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4432				&islept, NFSCLSTATEMUTEXPTR, NULL);
4433			    if (islept)
4434				break;
4435			}
4436			if (islept)
4437			    continue;
4438			NFSUNLOCKCLSTATE();
4439			cred = newnfs_getcred();
4440			newnfs_copycred(&dp->nfsdl_cred, cred);
4441			(void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0);
4442			NFSFREECRED(cred);
4443			triedrecall = 1;
4444			NFSLOCKCLSTATE();
4445			nfsv4_unlock(&clp->nfsc_lock, 0);
4446			igotlock = 0;
4447			continue;
4448		    }
4449		    *fstp = dp->nfsdl_stateid;
4450		    retcnt++;
4451		    *gotfdp = 1;
4452		    nfscl_cleandeleg(dp);
4453		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
4454		}
4455		if (igotlock) {
4456		    nfsv4_unlock(&clp->nfsc_lock, 0);
4457		    igotlock = 0;
4458		}
4459		if (tvp != NULL) {
4460		    np = VTONFS(tvp);
4461		    dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4462			np->n_fhp->nfh_len);
4463		    if (dp != NULL && *gottdp == 0) {
4464			/*
4465			 * Wait for outstanding I/O ops to be done.
4466			 */
4467			if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4468			    dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4469			    (void) nfsmsleep(&dp->nfsdl_rwlock,
4470				NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4471			    continue;
4472			}
4473			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4474			    if (!LIST_EMPTY(&owp->nfsow_open)) {
4475				NFSUNLOCKCLSTATE();
4476				return (retcnt);
4477			    }
4478			}
4479			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4480			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
4481				NFSUNLOCKCLSTATE();
4482				return (retcnt);
4483			    }
4484			}
4485			*tstp = dp->nfsdl_stateid;
4486			retcnt++;
4487			*gottdp = 1;
4488			nfscl_cleandeleg(dp);
4489			nfscl_freedeleg(&clp->nfsc_deleg, dp);
4490		    }
4491		}
4492		NFSUNLOCKCLSTATE();
4493		return (retcnt);
4494	}
4495}
4496
4497/*
4498 * Get a reference on the clientid associated with the mount point.
4499 * Return 1 if success, 0 otherwise.
4500 */
4501APPLESTATIC int
4502nfscl_getref(struct nfsmount *nmp)
4503{
4504	struct nfsclclient *clp;
4505
4506	NFSLOCKCLSTATE();
4507	clp = nfscl_findcl(nmp);
4508	if (clp == NULL) {
4509		NFSUNLOCKCLSTATE();
4510		return (0);
4511	}
4512	nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL);
4513	NFSUNLOCKCLSTATE();
4514	return (1);
4515}
4516
4517/*
4518 * Release a reference on a clientid acquired with the above call.
4519 */
4520APPLESTATIC void
4521nfscl_relref(struct nfsmount *nmp)
4522{
4523	struct nfsclclient *clp;
4524
4525	NFSLOCKCLSTATE();
4526	clp = nfscl_findcl(nmp);
4527	if (clp == NULL) {
4528		NFSUNLOCKCLSTATE();
4529		return;
4530	}
4531	nfsv4_relref(&clp->nfsc_lock);
4532	NFSUNLOCKCLSTATE();
4533}
4534
4535/*
4536 * Save the size attribute in the delegation, since the nfsnode
4537 * is going away.
4538 */
4539APPLESTATIC void
4540nfscl_reclaimnode(vnode_t vp)
4541{
4542	struct nfsclclient *clp;
4543	struct nfscldeleg *dp;
4544	struct nfsnode *np = VTONFS(vp);
4545	struct nfsmount *nmp;
4546
4547	nmp = VFSTONFS(vnode_mount(vp));
4548	if (!NFSHASNFSV4(nmp))
4549		return;
4550	NFSLOCKCLSTATE();
4551	clp = nfscl_findcl(nmp);
4552	if (clp == NULL) {
4553		NFSUNLOCKCLSTATE();
4554		return;
4555	}
4556	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4557	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4558		dp->nfsdl_size = np->n_size;
4559	NFSUNLOCKCLSTATE();
4560}
4561
4562/*
4563 * Get the saved size attribute in the delegation, since it is a
4564 * newly allocated nfsnode.
4565 */
4566APPLESTATIC void
4567nfscl_newnode(vnode_t vp)
4568{
4569	struct nfsclclient *clp;
4570	struct nfscldeleg *dp;
4571	struct nfsnode *np = VTONFS(vp);
4572	struct nfsmount *nmp;
4573
4574	nmp = VFSTONFS(vnode_mount(vp));
4575	if (!NFSHASNFSV4(nmp))
4576		return;
4577	NFSLOCKCLSTATE();
4578	clp = nfscl_findcl(nmp);
4579	if (clp == NULL) {
4580		NFSUNLOCKCLSTATE();
4581		return;
4582	}
4583	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4584	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4585		np->n_size = dp->nfsdl_size;
4586	NFSUNLOCKCLSTATE();
4587}
4588
4589/*
4590 * If there is a valid write delegation for this file, set the modtime
4591 * to the local clock time.
4592 */
4593APPLESTATIC void
4594nfscl_delegmodtime(vnode_t vp)
4595{
4596	struct nfsclclient *clp;
4597	struct nfscldeleg *dp;
4598	struct nfsnode *np = VTONFS(vp);
4599	struct nfsmount *nmp;
4600
4601	nmp = VFSTONFS(vnode_mount(vp));
4602	if (!NFSHASNFSV4(nmp))
4603		return;
4604	NFSLOCKCLSTATE();
4605	clp = nfscl_findcl(nmp);
4606	if (clp == NULL) {
4607		NFSUNLOCKCLSTATE();
4608		return;
4609	}
4610	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4611	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
4612		nanotime(&dp->nfsdl_modtime);
4613		dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
4614	}
4615	NFSUNLOCKCLSTATE();
4616}
4617
4618/*
4619 * If there is a valid write delegation for this file with a modtime set,
4620 * put that modtime in mtime.
4621 */
4622APPLESTATIC void
4623nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
4624{
4625	struct nfsclclient *clp;
4626	struct nfscldeleg *dp;
4627	struct nfsnode *np = VTONFS(vp);
4628	struct nfsmount *nmp;
4629
4630	nmp = VFSTONFS(vnode_mount(vp));
4631	if (!NFSHASNFSV4(nmp))
4632		return;
4633	NFSLOCKCLSTATE();
4634	clp = nfscl_findcl(nmp);
4635	if (clp == NULL) {
4636		NFSUNLOCKCLSTATE();
4637		return;
4638	}
4639	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4640	if (dp != NULL &&
4641	    (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
4642	    (NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
4643		*mtime = dp->nfsdl_modtime;
4644	NFSUNLOCKCLSTATE();
4645}
4646
4647static int
4648nfscl_errmap(struct nfsrv_descript *nd, u_int32_t minorvers)
4649{
4650	short *defaulterrp, *errp;
4651
4652	if (!nd->nd_repstat)
4653		return (0);
4654	if (nd->nd_procnum == NFSPROC_NOOP)
4655		return (txdr_unsigned(nd->nd_repstat & 0xffff));
4656	if (nd->nd_repstat == EBADRPC)
4657		return (txdr_unsigned(NFSERR_BADXDR));
4658	if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
4659	    nd->nd_repstat == NFSERR_OPILLEGAL)
4660		return (txdr_unsigned(nd->nd_repstat));
4661	if (nd->nd_repstat >= NFSERR_BADIOMODE && nd->nd_repstat < 20000 &&
4662	    minorvers > NFSV4_MINORVERSION) {
4663		/* NFSv4.n error. */
4664		return (txdr_unsigned(nd->nd_repstat));
4665	}
4666	if (nd->nd_procnum < NFSV4OP_CBNOPS)
4667		errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
4668	else
4669		return (txdr_unsigned(nd->nd_repstat));
4670	while (*++errp)
4671		if (*errp == (short)nd->nd_repstat)
4672			return (txdr_unsigned(nd->nd_repstat));
4673	return (txdr_unsigned(*defaulterrp));
4674}
4675
4676/*
4677 * Called to find/add a layout to a client.
4678 * This function returns the layout with a refcnt (shared lock) upon
4679 * success (returns 0) or with no lock/refcnt on the layout when an
4680 * error is returned.
4681 * If a layout is passed in via lypp, it is locked (exclusively locked).
4682 */
4683APPLESTATIC int
4684nfscl_layout(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
4685    nfsv4stateid_t *stateidp, int retonclose,
4686    struct nfsclflayouthead *fhlp, struct nfscllayout **lypp,
4687    struct ucred *cred, NFSPROC_T *p)
4688{
4689	struct nfsclclient *clp;
4690	struct nfscllayout *lyp, *tlyp;
4691	struct nfsclflayout *flp;
4692	struct nfsnode *np = VTONFS(vp);
4693	mount_t mp;
4694	int layout_passed_in;
4695
4696	mp = nmp->nm_mountp;
4697	layout_passed_in = 1;
4698	tlyp = NULL;
4699	lyp = *lypp;
4700	if (lyp == NULL) {
4701		layout_passed_in = 0;
4702		tlyp = malloc(sizeof(*tlyp) + fhlen - 1, M_NFSLAYOUT,
4703		    M_WAITOK | M_ZERO);
4704	}
4705
4706	NFSLOCKCLSTATE();
4707	clp = nmp->nm_clp;
4708	if (clp == NULL) {
4709		if (layout_passed_in != 0)
4710			nfsv4_unlock(&lyp->nfsly_lock, 0);
4711		NFSUNLOCKCLSTATE();
4712		if (tlyp != NULL)
4713			free(tlyp, M_NFSLAYOUT);
4714		return (EPERM);
4715	}
4716	if (lyp == NULL) {
4717		/*
4718		 * Although no lyp was passed in, another thread might have
4719		 * allocated one. If one is found, just increment it's ref
4720		 * count and return it.
4721		 */
4722		lyp = nfscl_findlayout(clp, fhp, fhlen);
4723		if (lyp == NULL) {
4724			lyp = tlyp;
4725			tlyp = NULL;
4726			lyp->nfsly_stateid.seqid = stateidp->seqid;
4727			lyp->nfsly_stateid.other[0] = stateidp->other[0];
4728			lyp->nfsly_stateid.other[1] = stateidp->other[1];
4729			lyp->nfsly_stateid.other[2] = stateidp->other[2];
4730			lyp->nfsly_lastbyte = 0;
4731			LIST_INIT(&lyp->nfsly_flayread);
4732			LIST_INIT(&lyp->nfsly_flayrw);
4733			LIST_INIT(&lyp->nfsly_recall);
4734			lyp->nfsly_filesid[0] = np->n_vattr.na_filesid[0];
4735			lyp->nfsly_filesid[1] = np->n_vattr.na_filesid[1];
4736			lyp->nfsly_clp = clp;
4737			lyp->nfsly_flags = (retonclose != 0) ?
4738			    (NFSLY_FILES | NFSLY_RETONCLOSE) : NFSLY_FILES;
4739			lyp->nfsly_fhlen = fhlen;
4740			NFSBCOPY(fhp, lyp->nfsly_fh, fhlen);
4741			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4742			LIST_INSERT_HEAD(NFSCLLAYOUTHASH(clp, fhp, fhlen), lyp,
4743			    nfsly_hash);
4744			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4745			nfscl_layoutcnt++;
4746		} else {
4747			if (retonclose != 0)
4748				lyp->nfsly_flags |= NFSLY_RETONCLOSE;
4749			TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
4750			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4751			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4752		}
4753		nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
4754		if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
4755			NFSUNLOCKCLSTATE();
4756			if (tlyp != NULL)
4757				free(tlyp, M_NFSLAYOUT);
4758			return (EPERM);
4759		}
4760		*lypp = lyp;
4761	} else
4762		lyp->nfsly_stateid.seqid = stateidp->seqid;
4763
4764	/* Merge the new list of File Layouts into the list. */
4765	flp = LIST_FIRST(fhlp);
4766	if (flp != NULL) {
4767		if (flp->nfsfl_iomode == NFSLAYOUTIOMODE_READ)
4768			nfscl_mergeflayouts(&lyp->nfsly_flayread, fhlp);
4769		else
4770			nfscl_mergeflayouts(&lyp->nfsly_flayrw, fhlp);
4771	}
4772	if (layout_passed_in != 0)
4773		nfsv4_unlock(&lyp->nfsly_lock, 1);
4774	NFSUNLOCKCLSTATE();
4775	if (tlyp != NULL)
4776		free(tlyp, M_NFSLAYOUT);
4777	return (0);
4778}
4779
4780/*
4781 * Search for a layout by MDS file handle.
4782 * If one is found, it is returned with a refcnt (shared lock) iff
4783 * retflpp returned non-NULL and locked (exclusive locked) iff retflpp is
4784 * returned NULL.
4785 */
4786struct nfscllayout *
4787nfscl_getlayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen,
4788    uint64_t off, struct nfsclflayout **retflpp, int *recalledp)
4789{
4790	struct nfscllayout *lyp;
4791	mount_t mp;
4792	int error, igotlock;
4793
4794	mp = clp->nfsc_nmp->nm_mountp;
4795	*recalledp = 0;
4796	*retflpp = NULL;
4797	NFSLOCKCLSTATE();
4798	lyp = nfscl_findlayout(clp, fhp, fhlen);
4799	if (lyp != NULL) {
4800		if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) {
4801			TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list);
4802			TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list);
4803			lyp->nfsly_timestamp = NFSD_MONOSEC + 120;
4804			error = nfscl_findlayoutforio(lyp, off,
4805			    NFSV4OPEN_ACCESSREAD, retflpp);
4806			if (error == 0)
4807				nfsv4_getref(&lyp->nfsly_lock, NULL,
4808				    NFSCLSTATEMUTEXPTR, mp);
4809			else {
4810				do {
4811					igotlock = nfsv4_lock(&lyp->nfsly_lock,
4812					    1, NULL, NFSCLSTATEMUTEXPTR, mp);
4813				} while (igotlock == 0 &&
4814				    (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0);
4815				*retflpp = NULL;
4816			}
4817			if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
4818				lyp = NULL;
4819				*recalledp = 1;
4820			}
4821		} else {
4822			lyp = NULL;
4823			*recalledp = 1;
4824		}
4825	}
4826	NFSUNLOCKCLSTATE();
4827	return (lyp);
4828}
4829
4830/*
4831 * Search for a layout by MDS file handle. If one is found that is marked
4832 * "return on close", delete it, since it should now be forgotten.
4833 */
4834static void
4835nfscl_retoncloselayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen)
4836{
4837	struct nfscllayout *lyp;
4838
4839tryagain:
4840	lyp = nfscl_findlayout(clp, fhp, fhlen);
4841	if (lyp != NULL && (lyp->nfsly_flags & NFSLY_RETONCLOSE) != 0) {
4842		/*
4843		 * Wait for outstanding I/O ops to be done.
4844		 */
4845		if (lyp->nfsly_lock.nfslock_usecnt != 0 ||
4846		    lyp->nfsly_lock.nfslock_lock != 0) {
4847			lyp->nfsly_lock.nfslock_lock |= NFSV4LOCK_WANTED;
4848			(void)mtx_sleep(&lyp->nfsly_lock,
4849			    NFSCLSTATEMUTEXPTR, PZERO, "nfslyc", 0);
4850			goto tryagain;
4851		}
4852		nfscl_freelayout(lyp);
4853	}
4854}
4855
4856/*
4857 * Dereference a layout.
4858 */
4859void
4860nfscl_rellayout(struct nfscllayout *lyp, int exclocked)
4861{
4862
4863	NFSLOCKCLSTATE();
4864	if (exclocked != 0)
4865		nfsv4_unlock(&lyp->nfsly_lock, 0);
4866	else
4867		nfsv4_relref(&lyp->nfsly_lock);
4868	NFSUNLOCKCLSTATE();
4869}
4870
4871/*
4872 * Search for a devinfo by deviceid. If one is found, return it after
4873 * acquiring a reference count on it.
4874 */
4875struct nfscldevinfo *
4876nfscl_getdevinfo(struct nfsclclient *clp, uint8_t *deviceid,
4877    struct nfscldevinfo *dip)
4878{
4879
4880	NFSLOCKCLSTATE();
4881	if (dip == NULL)
4882		dip = nfscl_finddevinfo(clp, deviceid);
4883	if (dip != NULL)
4884		dip->nfsdi_refcnt++;
4885	NFSUNLOCKCLSTATE();
4886	return (dip);
4887}
4888
4889/*
4890 * Dereference a devinfo structure.
4891 */
4892static void
4893nfscl_reldevinfo_locked(struct nfscldevinfo *dip)
4894{
4895
4896	dip->nfsdi_refcnt--;
4897	if (dip->nfsdi_refcnt == 0)
4898		wakeup(&dip->nfsdi_refcnt);
4899}
4900
4901/*
4902 * Dereference a devinfo structure.
4903 */
4904void
4905nfscl_reldevinfo(struct nfscldevinfo *dip)
4906{
4907
4908	NFSLOCKCLSTATE();
4909	nfscl_reldevinfo_locked(dip);
4910	NFSUNLOCKCLSTATE();
4911}
4912
4913/*
4914 * Find a layout for this file handle. Return NULL upon failure.
4915 */
4916static struct nfscllayout *
4917nfscl_findlayout(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
4918{
4919	struct nfscllayout *lyp;
4920
4921	LIST_FOREACH(lyp, NFSCLLAYOUTHASH(clp, fhp, fhlen), nfsly_hash)
4922		if (lyp->nfsly_fhlen == fhlen &&
4923		    !NFSBCMP(lyp->nfsly_fh, fhp, fhlen))
4924			break;
4925	return (lyp);
4926}
4927
4928/*
4929 * Find a devinfo for this deviceid. Return NULL upon failure.
4930 */
4931static struct nfscldevinfo *
4932nfscl_finddevinfo(struct nfsclclient *clp, uint8_t *deviceid)
4933{
4934	struct nfscldevinfo *dip;
4935
4936	LIST_FOREACH(dip, &clp->nfsc_devinfo, nfsdi_list)
4937		if (NFSBCMP(dip->nfsdi_deviceid, deviceid, NFSX_V4DEVICEID)
4938		    == 0)
4939			break;
4940	return (dip);
4941}
4942
4943/*
4944 * Merge the new file layout list into the main one, maintaining it in
4945 * increasing offset order.
4946 */
4947static void
4948nfscl_mergeflayouts(struct nfsclflayouthead *fhlp,
4949    struct nfsclflayouthead *newfhlp)
4950{
4951	struct nfsclflayout *flp, *nflp, *prevflp, *tflp;
4952
4953	flp = LIST_FIRST(fhlp);
4954	prevflp = NULL;
4955	LIST_FOREACH_SAFE(nflp, newfhlp, nfsfl_list, tflp) {
4956		while (flp != NULL && flp->nfsfl_off < nflp->nfsfl_off) {
4957			prevflp = flp;
4958			flp = LIST_NEXT(flp, nfsfl_list);
4959		}
4960		if (prevflp == NULL)
4961			LIST_INSERT_HEAD(fhlp, nflp, nfsfl_list);
4962		else
4963			LIST_INSERT_AFTER(prevflp, nflp, nfsfl_list);
4964		prevflp = nflp;
4965	}
4966}
4967
4968/*
4969 * Add this nfscldevinfo to the client, if it doesn't already exist.
4970 * This function consumes the structure pointed at by dip, if not NULL.
4971 */
4972APPLESTATIC int
4973nfscl_adddevinfo(struct nfsmount *nmp, struct nfscldevinfo *dip,
4974    struct nfsclflayout *flp)
4975{
4976	struct nfsclclient *clp;
4977	struct nfscldevinfo *tdip;
4978
4979	NFSLOCKCLSTATE();
4980	clp = nmp->nm_clp;
4981	if (clp == NULL) {
4982		NFSUNLOCKCLSTATE();
4983		if (dip != NULL)
4984			free(dip, M_NFSDEVINFO);
4985		return (ENODEV);
4986	}
4987	tdip = nfscl_finddevinfo(clp, flp->nfsfl_dev);
4988	if (tdip != NULL) {
4989		tdip->nfsdi_layoutrefs++;
4990		flp->nfsfl_devp = tdip;
4991		nfscl_reldevinfo_locked(tdip);
4992		NFSUNLOCKCLSTATE();
4993		if (dip != NULL)
4994			free(dip, M_NFSDEVINFO);
4995		return (0);
4996	}
4997	if (dip != NULL) {
4998		LIST_INSERT_HEAD(&clp->nfsc_devinfo, dip, nfsdi_list);
4999		dip->nfsdi_layoutrefs = 1;
5000		flp->nfsfl_devp = dip;
5001	}
5002	NFSUNLOCKCLSTATE();
5003	if (dip == NULL)
5004		return (ENODEV);
5005	return (0);
5006}
5007
5008/*
5009 * Free up a layout structure and associated file layout structure(s).
5010 */
5011APPLESTATIC void
5012nfscl_freelayout(struct nfscllayout *layp)
5013{
5014	struct nfsclflayout *flp, *nflp;
5015	struct nfsclrecalllayout *rp, *nrp;
5016
5017	LIST_FOREACH_SAFE(flp, &layp->nfsly_flayread, nfsfl_list, nflp) {
5018		LIST_REMOVE(flp, nfsfl_list);
5019		nfscl_freeflayout(flp);
5020	}
5021	LIST_FOREACH_SAFE(flp, &layp->nfsly_flayrw, nfsfl_list, nflp) {
5022		LIST_REMOVE(flp, nfsfl_list);
5023		nfscl_freeflayout(flp);
5024	}
5025	LIST_FOREACH_SAFE(rp, &layp->nfsly_recall, nfsrecly_list, nrp) {
5026		LIST_REMOVE(rp, nfsrecly_list);
5027		free(rp, M_NFSLAYRECALL);
5028	}
5029	nfscl_layoutcnt--;
5030	free(layp, M_NFSLAYOUT);
5031}
5032
5033/*
5034 * Free up a file layout structure.
5035 */
5036APPLESTATIC void
5037nfscl_freeflayout(struct nfsclflayout *flp)
5038{
5039	int i;
5040
5041	for (i = 0; i < flp->nfsfl_fhcnt; i++)
5042		free(flp->nfsfl_fh[i], M_NFSFH);
5043	if (flp->nfsfl_devp != NULL)
5044		flp->nfsfl_devp->nfsdi_layoutrefs--;
5045	free(flp, M_NFSFLAYOUT);
5046}
5047
5048/*
5049 * Free up a file layout devinfo structure.
5050 */
5051APPLESTATIC void
5052nfscl_freedevinfo(struct nfscldevinfo *dip)
5053{
5054
5055	free(dip, M_NFSDEVINFO);
5056}
5057
5058/*
5059 * Mark any layouts that match as recalled.
5060 */
5061static int
5062nfscl_layoutrecall(int recalltype, struct nfscllayout *lyp, uint32_t iomode,
5063    uint64_t off, uint64_t len, uint32_t stateseqid,
5064    struct nfsclrecalllayout *recallp)
5065{
5066	struct nfsclrecalllayout *rp, *orp;
5067
5068	recallp->nfsrecly_recalltype = recalltype;
5069	recallp->nfsrecly_iomode = iomode;
5070	recallp->nfsrecly_stateseqid = stateseqid;
5071	recallp->nfsrecly_off = off;
5072	recallp->nfsrecly_len = len;
5073	/*
5074	 * Order the list as file returns first, followed by fsid and any
5075	 * returns, both in increasing stateseqid order.
5076	 * Note that the seqids wrap around, so 1 is after 0xffffffff.
5077	 * (I'm not sure this is correct because I find RFC5661 confusing
5078	 *  on this, but hopefully it will work ok.)
5079	 */
5080	orp = NULL;
5081	LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5082		orp = rp;
5083		if ((recalltype == NFSLAYOUTRETURN_FILE &&
5084		     (rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE ||
5085		      nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) ||
5086		    (recalltype != NFSLAYOUTRETURN_FILE &&
5087		     rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE &&
5088		     nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) {
5089			LIST_INSERT_BEFORE(rp, recallp, nfsrecly_list);
5090			break;
5091		}
5092	}
5093	if (rp == NULL) {
5094		if (orp == NULL)
5095			LIST_INSERT_HEAD(&lyp->nfsly_recall, recallp,
5096			    nfsrecly_list);
5097		else
5098			LIST_INSERT_AFTER(orp, recallp, nfsrecly_list);
5099	}
5100	lyp->nfsly_flags |= NFSLY_RECALL;
5101	return (0);
5102}
5103
5104/*
5105 * Compare the two seqids for ordering. The trick is that the seqids can
5106 * wrap around from 0xffffffff->0, so check for the cases where one
5107 * has wrapped around.
5108 * Return 1 if seqid1 comes before seqid2, 0 otherwise.
5109 */
5110static int
5111nfscl_seq(uint32_t seqid1, uint32_t seqid2)
5112{
5113
5114	if (seqid2 > seqid1 && (seqid2 - seqid1) >= 0x7fffffff)
5115		/* seqid2 has wrapped around. */
5116		return (0);
5117	if (seqid1 > seqid2 && (seqid1 - seqid2) >= 0x7fffffff)
5118		/* seqid1 has wrapped around. */
5119		return (1);
5120	if (seqid1 <= seqid2)
5121		return (1);
5122	return (0);
5123}
5124
5125/*
5126 * Do a layout return for each of the recalls.
5127 */
5128static void
5129nfscl_layoutreturn(struct nfsmount *nmp, struct nfscllayout *lyp,
5130    struct ucred *cred, NFSPROC_T *p)
5131{
5132	struct nfsclrecalllayout *rp;
5133	nfsv4stateid_t stateid;
5134
5135	NFSBCOPY(lyp->nfsly_stateid.other, stateid.other, NFSX_STATEIDOTHER);
5136	LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) {
5137		stateid.seqid = rp->nfsrecly_stateseqid;
5138		(void)nfsrpc_layoutreturn(nmp, lyp->nfsly_fh,
5139		    lyp->nfsly_fhlen, 0, NFSLAYOUT_NFSV4_1_FILES,
5140		    rp->nfsrecly_iomode, rp->nfsrecly_recalltype,
5141		    rp->nfsrecly_off, rp->nfsrecly_len,
5142		    &stateid, 0, NULL, cred, p, NULL);
5143	}
5144}
5145
5146/*
5147 * Do the layout commit for a file layout.
5148 */
5149static void
5150nfscl_dolayoutcommit(struct nfsmount *nmp, struct nfscllayout *lyp,
5151    struct ucred *cred, NFSPROC_T *p)
5152{
5153	struct nfsclflayout *flp;
5154	uint64_t len;
5155	int error;
5156
5157	LIST_FOREACH(flp, &lyp->nfsly_flayrw, nfsfl_list) {
5158		if (flp->nfsfl_off <= lyp->nfsly_lastbyte) {
5159			len = flp->nfsfl_end - flp->nfsfl_off;
5160			error = nfsrpc_layoutcommit(nmp, lyp->nfsly_fh,
5161			    lyp->nfsly_fhlen, 0, flp->nfsfl_off, len,
5162			    lyp->nfsly_lastbyte, &lyp->nfsly_stateid,
5163			    NFSLAYOUT_NFSV4_1_FILES, 0, NULL, cred, p, NULL);
5164			NFSCL_DEBUG(4, "layoutcommit err=%d\n", error);
5165			if (error == NFSERR_NOTSUPP) {
5166				/* If not supported, don't bother doing it. */
5167				NFSLOCKMNT(nmp);
5168				nmp->nm_state |= NFSSTA_NOLAYOUTCOMMIT;
5169				NFSUNLOCKMNT(nmp);
5170				break;
5171			}
5172		}
5173	}
5174}
5175
5176/*
5177 * Commit all layouts for a file (vnode).
5178 */
5179int
5180nfscl_layoutcommit(vnode_t vp, NFSPROC_T *p)
5181{
5182	struct nfsclclient *clp;
5183	struct nfscllayout *lyp;
5184	struct nfsnode *np = VTONFS(vp);
5185	mount_t mp;
5186	struct nfsmount *nmp;
5187
5188	mp = vnode_mount(vp);
5189	nmp = VFSTONFS(mp);
5190	if (NFSHASNOLAYOUTCOMMIT(nmp))
5191		return (0);
5192	NFSLOCKCLSTATE();
5193	clp = nmp->nm_clp;
5194	if (clp == NULL) {
5195		NFSUNLOCKCLSTATE();
5196		return (EPERM);
5197	}
5198	lyp = nfscl_findlayout(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
5199	if (lyp == NULL) {
5200		NFSUNLOCKCLSTATE();
5201		return (EPERM);
5202	}
5203	nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
5204	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
5205		NFSUNLOCKCLSTATE();
5206		return (EPERM);
5207	}
5208tryagain:
5209	if ((lyp->nfsly_flags & NFSLY_WRITTEN) != 0) {
5210		lyp->nfsly_flags &= ~NFSLY_WRITTEN;
5211		NFSUNLOCKCLSTATE();
5212		NFSCL_DEBUG(4, "do layoutcommit2\n");
5213		nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, NFSPROCCRED(p), p);
5214		NFSLOCKCLSTATE();
5215		goto tryagain;
5216	}
5217	nfsv4_relref(&lyp->nfsly_lock);
5218	NFSUNLOCKCLSTATE();
5219	return (0);
5220}
5221
5222