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