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