nfs_clstate.c revision 201439
1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 2009 Rick Macklem, University of Guelph
3191783Srmacklem * All rights reserved.
4191783Srmacklem *
5191783Srmacklem * Redistribution and use in source and binary forms, with or without
6191783Srmacklem * modification, are permitted provided that the following conditions
7191783Srmacklem * are met:
8191783Srmacklem * 1. Redistributions of source code must retain the above copyright
9191783Srmacklem *    notice, this list of conditions and the following disclaimer.
10191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
11191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
12191783Srmacklem *    documentation and/or other materials provided with the distribution.
13191783Srmacklem *
14191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24191783Srmacklem * SUCH DAMAGE.
25191783Srmacklem *
26191783Srmacklem */
27191783Srmacklem
28191783Srmacklem#include <sys/cdefs.h>
29191783Srmacklem__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clstate.c 201439 2010-01-03 18:27:10Z rmacklem $");
30191783Srmacklem
31191783Srmacklem/*
32191783Srmacklem * These functions implement the client side state handling for NFSv4.
33191783Srmacklem * NFSv4 state handling:
34191783Srmacklem * - A lockowner is used to determine lock contention, so it
35191783Srmacklem *   corresponds directly to a Posix pid. (1 to 1 mapping)
36191783Srmacklem * - The correct granularity of an OpenOwner is not nearly so
37191783Srmacklem *   obvious. An OpenOwner does the following:
38191783Srmacklem *   - provides a serial sequencing of Open/Close/Lock-with-new-lockowner
39198289Sjh *   - is used to check for Open/Share contention (not applicable to
40191783Srmacklem *     this client, since all Opens are Deny_None)
41198289Sjh *   As such, I considered both extreme.
42191783Srmacklem *   1 OpenOwner per ClientID - Simple to manage, but fully serializes
43191783Srmacklem *   all Open, Close and Lock (with a new lockowner) Ops.
44191783Srmacklem *   1 OpenOwner for each Open - This one results in an OpenConfirm for
45191783Srmacklem *   every Open, for most servers.
46191783Srmacklem *   So, I chose to use the same mapping as I did for LockOwnwers.
47191783Srmacklem *   The main concern here is that you can end up with multiple Opens
48191783Srmacklem *   for the same File Handle, but on different OpenOwners (opens
49191783Srmacklem *   inherited from parents, grandparents...) and you do not know
50191783Srmacklem *   which of these the vnodeop close applies to. This is handled by
51191783Srmacklem *   delaying the Close Op(s) until all of the Opens have been closed.
52191783Srmacklem *   (It is not yet obvious if this is the correct granularity.)
53198289Sjh * - How the code handles serialization:
54198289Sjh *   - For the ClientId, it uses an exclusive lock while getting its
55191783Srmacklem *     SetClientId and during recovery. Otherwise, it uses a shared
56191783Srmacklem *     lock via a reference count.
57191783Srmacklem *   - For the rest of the data structures, it uses an SMP mutex
58191783Srmacklem *     (once the nfs client is SMP safe) and doesn't sleep while
59191783Srmacklem *     manipulating the linked lists.
60191783Srmacklem *   - The serialization of Open/Close/Lock/LockU falls out in the
61191783Srmacklem *     "wash", since OpenOwners and LockOwners are both mapped from
62191783Srmacklem *     Posix pid. In other words, there is only one Posix pid using
63191783Srmacklem *     any given owner, so that owner is serialized. (If you change
64191783Srmacklem *     the granularity of the OpenOwner, then code must be added to
65191783Srmacklem *     serialize Ops on the OpenOwner.)
66191783Srmacklem * - When to get rid of OpenOwners and LockOwners.
67191783Srmacklem *   - When a process exits, it calls nfscl_cleanup(), which goes
68191783Srmacklem *     through the client list looking for all Open and Lock Owners.
69191783Srmacklem *     When one is found, it is marked "defunct" or in the case of
70191783Srmacklem *     an OpenOwner without any Opens, freed.
71191783Srmacklem *     The renew thread scans for defunct Owners and gets rid of them,
72191783Srmacklem *     if it can. The LockOwners will also be deleted when the
73191783Srmacklem *     associated Open is closed.
74191783Srmacklem *   - If the LockU or Close Op(s) fail during close in a way
75191783Srmacklem *     that could be recovered upon retry, they are relinked to the
76191783Srmacklem *     ClientId's defunct open list and retried by the renew thread
77191783Srmacklem *     until they succeed or an unmount/recovery occurs.
78191783Srmacklem *     (Since we are done with them, they do not need to be recovered.)
79191783Srmacklem */
80191783Srmacklem
81191783Srmacklem#ifndef APPLEKEXT
82191783Srmacklem#include <fs/nfs/nfsport.h>
83191783Srmacklem
84191783Srmacklem/*
85191783Srmacklem * Global variables
86191783Srmacklem */
87191783Srmacklemextern struct nfsstats newnfsstats;
88191783Srmacklemextern struct nfsreqhead nfsd_reqq;
89191783SrmacklemNFSREQSPINLOCK;
90191783SrmacklemNFSCLSTATEMUTEX;
91191783Srmacklemint nfscl_inited = 0;
92191783Srmacklemstruct nfsclhead nfsclhead;	/* Head of clientid list */
93191783Srmacklemint nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
94191783Srmacklem#endif	/* !APPLEKEXT */
95191783Srmacklem
96191783Srmacklemstatic int nfscl_delegcnt = 0;
97191783Srmacklemstatic int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *,
98191783Srmacklem    NFSPROC_T *, u_int32_t, struct nfsclowner **, struct nfsclopen **);
99191783Srmacklemstatic void nfscl_clrelease(struct nfsclclient *);
100191783Srmacklemstatic void nfscl_cleanclient(struct nfsclclient *);
101191783Srmacklemstatic void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
102191783Srmacklem    struct ucred *, NFSPROC_T *);
103191783Srmacklemstatic int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
104191783Srmacklem    struct nfsmount *, struct ucred *, NFSPROC_T *);
105191783Srmacklemstatic void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *);
106191783Srmacklemstatic void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
107191783Srmacklem    struct nfscllock *, int);
108191783Srmacklemstatic int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
109191783Srmacklem    struct nfscllock **, int);
110191783Srmacklemstatic void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *);
111191783Srmacklemstatic u_int32_t nfscl_nextcbident(void);
112191783Srmacklemstatic mount_t nfscl_getmnt(u_int32_t);
113191783Srmacklemstatic struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *,
114191783Srmacklem    int);
115191783Srmacklemstatic int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *,
116191783Srmacklem    u_int8_t *, struct nfscllock **);
117191783Srmacklemstatic void nfscl_freelockowner(struct nfscllockowner *, int);
118191783Srmacklemstatic void nfscl_freealllocks(struct nfscllockownerhead *, int);
119201439Srmacklemstatic int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int,
120201439Srmacklem    struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **);
121191783Srmacklemstatic void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *,
122191783Srmacklem    struct nfsclowner **, struct nfsclowner **, struct nfsclopen **,
123191783Srmacklem    struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *);
124191783Srmacklemstatic int nfscl_moveopen(vnode_t , struct nfsclclient *,
125191783Srmacklem    struct nfsmount *, struct nfsclopen *, struct nfsclowner *,
126191783Srmacklem    struct nfscldeleg *, struct ucred *, NFSPROC_T *);
127191783Srmacklemstatic void nfscl_totalrecall(struct nfsclclient *);
128191783Srmacklemstatic int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *,
129191783Srmacklem    struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *);
130191783Srmacklemstatic int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int,
131191783Srmacklem    u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int,
132191783Srmacklem    struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *);
133191783Srmacklemstatic int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
134191783Srmacklem    int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short,
135191783Srmacklem    struct ucred *, NFSPROC_T *);
136191783Srmacklemstatic int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
137191783Srmacklem    struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
138191783Srmacklemstatic void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *);
139191783Srmacklemstatic int nfscl_errmap(struct nfsrv_descript *);
140191783Srmacklemstatic void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
141191783Srmacklemstatic int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
142191783Srmacklem    struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *);
143191783Srmacklemstatic void nfscl_freeopenowner(struct nfsclowner *, int);
144191783Srmacklemstatic void nfscl_cleandeleg(struct nfscldeleg *);
145191783Srmacklemstatic int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
146191783Srmacklem    struct nfsmount *, NFSPROC_T *);
147191783Srmacklem
148191783Srmacklemstatic short nfscberr_null[] = {
149191783Srmacklem	0,
150191783Srmacklem	0,
151191783Srmacklem};
152191783Srmacklem
153191783Srmacklemstatic short nfscberr_getattr[] = {
154191783Srmacklem	NFSERR_RESOURCE,
155191783Srmacklem	NFSERR_BADHANDLE,
156191783Srmacklem	NFSERR_BADXDR,
157191783Srmacklem	NFSERR_RESOURCE,
158191783Srmacklem	NFSERR_SERVERFAULT,
159191783Srmacklem	0,
160191783Srmacklem};
161191783Srmacklem
162191783Srmacklemstatic short nfscberr_recall[] = {
163191783Srmacklem	NFSERR_RESOURCE,
164191783Srmacklem	NFSERR_BADHANDLE,
165191783Srmacklem	NFSERR_BADSTATEID,
166191783Srmacklem	NFSERR_BADXDR,
167191783Srmacklem	NFSERR_RESOURCE,
168191783Srmacklem	NFSERR_SERVERFAULT,
169191783Srmacklem	0,
170191783Srmacklem};
171191783Srmacklem
172191783Srmacklemstatic short *nfscl_cberrmap[] = {
173191783Srmacklem	nfscberr_null,
174191783Srmacklem	nfscberr_null,
175191783Srmacklem	nfscberr_null,
176191783Srmacklem	nfscberr_getattr,
177191783Srmacklem	nfscberr_recall
178191783Srmacklem};
179191783Srmacklem
180191783Srmacklem#define	NETFAMILY(clp) \
181191783Srmacklem		(((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET)
182191783Srmacklem
183191783Srmacklem/*
184191783Srmacklem * Called for an open operation.
185191783Srmacklem * If the nfhp argument is NULL, just get an openowner.
186191783Srmacklem */
187191783SrmacklemAPPLESTATIC int
188191783Srmacklemnfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg,
189191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp,
190191783Srmacklem    struct nfsclopen **opp, int *newonep, int *retp, int lockit)
191191783Srmacklem{
192191783Srmacklem	struct nfsclclient *clp;
193191783Srmacklem	struct nfsclowner *owp, *nowp;
194191783Srmacklem	struct nfsclopen *op = NULL, *nop = NULL;
195191783Srmacklem	struct nfscldeleg *dp;
196191783Srmacklem	struct nfsclownerhead *ohp;
197191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
198191783Srmacklem	int ret;
199191783Srmacklem
200191783Srmacklem	if (newonep != NULL)
201191783Srmacklem		*newonep = 0;
202191783Srmacklem	if (opp != NULL)
203191783Srmacklem		*opp = NULL;
204191783Srmacklem	if (owpp != NULL)
205191783Srmacklem		*owpp = NULL;
206191783Srmacklem
207191783Srmacklem	/*
208191783Srmacklem	 * Might need one or both of these, so MALLOC them now, to
209191783Srmacklem	 * avoid a tsleep() in MALLOC later.
210191783Srmacklem	 */
211191783Srmacklem	MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner),
212191783Srmacklem	    M_NFSCLOWNER, M_WAITOK);
213191783Srmacklem	if (nfhp != NULL)
214191783Srmacklem	    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
215191783Srmacklem		fhlen - 1, M_NFSCLOPEN, M_WAITOK);
216191783Srmacklem	ret = nfscl_getcl(vp, cred, p, &clp);
217191783Srmacklem	if (ret != 0) {
218191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
219191783Srmacklem		if (nop != NULL)
220191783Srmacklem			FREE((caddr_t)nop, M_NFSCLOPEN);
221191783Srmacklem		return (ret);
222191783Srmacklem	}
223191783Srmacklem
224191783Srmacklem	/*
225191783Srmacklem	 * Get the Open iff it already exists.
226191783Srmacklem	 * If none found, add the new one or return error, depending upon
227191783Srmacklem	 * "create".
228191783Srmacklem	 */
229191783Srmacklem	nfscl_filllockowner(p, own);
230191783Srmacklem	NFSLOCKCLSTATE();
231191783Srmacklem	dp = NULL;
232191783Srmacklem	/* First check the delegation list */
233191783Srmacklem	if (nfhp != NULL && usedeleg) {
234191783Srmacklem		LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
235191783Srmacklem			if (dp->nfsdl_fhlen == fhlen &&
236191783Srmacklem			    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
237191783Srmacklem				if (!(amode & NFSV4OPEN_ACCESSWRITE) ||
238191783Srmacklem				    (dp->nfsdl_flags & NFSCLDL_WRITE))
239191783Srmacklem					break;
240191783Srmacklem				dp = NULL;
241191783Srmacklem				break;
242191783Srmacklem			}
243191783Srmacklem		}
244191783Srmacklem	}
245191783Srmacklem
246191783Srmacklem	if (dp != NULL)
247191783Srmacklem		ohp = &dp->nfsdl_owner;
248191783Srmacklem	else
249191783Srmacklem		ohp = &clp->nfsc_owner;
250191783Srmacklem	/* Now, search for an openowner */
251191783Srmacklem	LIST_FOREACH(owp, ohp, nfsow_list) {
252191783Srmacklem		if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN))
253191783Srmacklem			break;
254191783Srmacklem	}
255191783Srmacklem
256191783Srmacklem	/*
257191783Srmacklem	 * Create a new open, as required.
258191783Srmacklem	 */
259191783Srmacklem	nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen,
260191783Srmacklem	    newonep);
261191783Srmacklem
262191783Srmacklem	/*
263191783Srmacklem	 * Serialize modifications to the open owner for multiple threads
264191783Srmacklem	 * within the same process using a read/write sleep lock.
265191783Srmacklem	 */
266191783Srmacklem	if (lockit)
267191783Srmacklem		nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
268191783Srmacklem	NFSUNLOCKCLSTATE();
269191783Srmacklem	if (nowp != NULL)
270191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
271191783Srmacklem	if (nop != NULL)
272191783Srmacklem		FREE((caddr_t)nop, M_NFSCLOPEN);
273191783Srmacklem	if (owpp != NULL)
274191783Srmacklem		*owpp = owp;
275191783Srmacklem	if (opp != NULL)
276191783Srmacklem		*opp = op;
277191783Srmacklem	if (retp != NULL)
278191783Srmacklem		*retp = NFSCLOPEN_OK;
279191783Srmacklem
280191783Srmacklem	/*
281191783Srmacklem	 * Now, check the mode on the open and return the appropriate
282191783Srmacklem	 * value.
283191783Srmacklem	 */
284191783Srmacklem	if (op != NULL && (amode & ~(op->nfso_mode))) {
285191783Srmacklem		op->nfso_mode |= amode;
286191783Srmacklem		if (retp != NULL && dp == NULL)
287191783Srmacklem			*retp = NFSCLOPEN_DOOPEN;
288191783Srmacklem	}
289191783Srmacklem	return (0);
290191783Srmacklem}
291191783Srmacklem
292191783Srmacklem/*
293191783Srmacklem * Create a new open, as required.
294191783Srmacklem */
295191783Srmacklemstatic void
296191783Srmacklemnfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp,
297191783Srmacklem    struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp,
298191783Srmacklem    struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen,
299191783Srmacklem    int *newonep)
300191783Srmacklem{
301191783Srmacklem	struct nfsclowner *owp = *owpp, *nowp;
302191783Srmacklem	struct nfsclopen *op, *nop;
303191783Srmacklem
304191783Srmacklem	if (nowpp != NULL)
305191783Srmacklem		nowp = *nowpp;
306191783Srmacklem	else
307191783Srmacklem		nowp = NULL;
308191783Srmacklem	if (nopp != NULL)
309191783Srmacklem		nop = *nopp;
310191783Srmacklem	else
311191783Srmacklem		nop = NULL;
312191783Srmacklem	if (owp == NULL && nowp != NULL) {
313191783Srmacklem		NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
314191783Srmacklem		LIST_INIT(&nowp->nfsow_open);
315191783Srmacklem		nowp->nfsow_clp = clp;
316191783Srmacklem		nowp->nfsow_seqid = 0;
317191783Srmacklem		nowp->nfsow_defunct = 0;
318191783Srmacklem		nfscl_lockinit(&nowp->nfsow_rwlock);
319191783Srmacklem		if (dp != NULL) {
320191783Srmacklem			newnfsstats.cllocalopenowners++;
321191783Srmacklem			LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list);
322191783Srmacklem		} else {
323191783Srmacklem			newnfsstats.clopenowners++;
324191783Srmacklem			LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list);
325191783Srmacklem		}
326191783Srmacklem		owp = *owpp = nowp;
327191783Srmacklem		*nowpp = NULL;
328191783Srmacklem		if (newonep != NULL)
329191783Srmacklem			*newonep = 1;
330191783Srmacklem	}
331191783Srmacklem
332191783Srmacklem	 /* If an fhp has been specified, create an Open as well. */
333191783Srmacklem	if (fhp != NULL) {
334191783Srmacklem		/* and look for the correct open, based upon FH */
335191783Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
336191783Srmacklem			if (op->nfso_fhlen == fhlen &&
337191783Srmacklem			    !NFSBCMP(op->nfso_fh, fhp, fhlen))
338191783Srmacklem				break;
339191783Srmacklem		}
340191783Srmacklem		if (op == NULL && nop != NULL) {
341191783Srmacklem			nop->nfso_own = owp;
342191783Srmacklem			nop->nfso_mode = 0;
343191783Srmacklem			nop->nfso_opencnt = 0;
344191783Srmacklem			nop->nfso_posixlock = 1;
345191783Srmacklem			nop->nfso_fhlen = fhlen;
346191783Srmacklem			NFSBCOPY(fhp, nop->nfso_fh, fhlen);
347191783Srmacklem			LIST_INIT(&nop->nfso_lock);
348191783Srmacklem			nop->nfso_stateid.seqid = 0;
349191783Srmacklem			nop->nfso_stateid.other[0] = 0;
350191783Srmacklem			nop->nfso_stateid.other[1] = 0;
351191783Srmacklem			nop->nfso_stateid.other[2] = 0;
352191783Srmacklem			if (dp != NULL) {
353191783Srmacklem				TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
354191783Srmacklem				TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
355191783Srmacklem				    nfsdl_list);
356191783Srmacklem				dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
357191783Srmacklem				newnfsstats.cllocalopens++;
358191783Srmacklem			} else {
359191783Srmacklem				newnfsstats.clopens++;
360191783Srmacklem			}
361191783Srmacklem			LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list);
362191783Srmacklem			*opp = nop;
363191783Srmacklem			*nopp = NULL;
364191783Srmacklem			if (newonep != NULL)
365191783Srmacklem				*newonep = 1;
366191783Srmacklem		} else {
367191783Srmacklem			*opp = op;
368191783Srmacklem		}
369191783Srmacklem	}
370191783Srmacklem}
371191783Srmacklem
372191783Srmacklem/*
373191783Srmacklem * Called to find/add a delegation to a client.
374191783Srmacklem */
375191783SrmacklemAPPLESTATIC int
376191783Srmacklemnfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
377191783Srmacklem    int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp)
378191783Srmacklem{
379191783Srmacklem	struct nfscldeleg *dp = *dpp, *tdp;
380191783Srmacklem
381191783Srmacklem	/*
382191783Srmacklem	 * First, if we have received a Read delegation for a file on a
383191783Srmacklem	 * read/write file system, just return it, because they aren't
384191783Srmacklem	 * useful, imho.
385191783Srmacklem	 */
386191783Srmacklem	if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) &&
387191783Srmacklem	    (dp->nfsdl_flags & NFSCLDL_READ)) {
388191783Srmacklem		(void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p);
389191783Srmacklem		FREE((caddr_t)dp, M_NFSCLDELEG);
390191783Srmacklem		*dpp = NULL;
391191783Srmacklem		return (0);
392191783Srmacklem	}
393191783Srmacklem
394191783Srmacklem	/* Look for the correct deleg, based upon FH */
395191783Srmacklem	NFSLOCKCLSTATE();
396191783Srmacklem	tdp = nfscl_finddeleg(clp, nfhp, fhlen);
397191783Srmacklem	if (tdp == NULL) {
398191783Srmacklem		if (dp == NULL) {
399191783Srmacklem			NFSUNLOCKCLSTATE();
400191783Srmacklem			return (NFSERR_BADSTATEID);
401191783Srmacklem		}
402191783Srmacklem		*dpp = NULL;
403191783Srmacklem		TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
404191783Srmacklem		LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp,
405191783Srmacklem		    nfsdl_hash);
406191783Srmacklem		dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
407191783Srmacklem		newnfsstats.cldelegates++;
408191783Srmacklem		nfscl_delegcnt++;
409191783Srmacklem	} else {
410191783Srmacklem		/*
411191783Srmacklem		 * Delegation already exists, what do we do if a new one??
412191783Srmacklem		 */
413191783Srmacklem		if (dp != NULL) {
414191783Srmacklem			printf("Deleg already exists!\n");
415191783Srmacklem			FREE((caddr_t)dp, M_NFSCLDELEG);
416191783Srmacklem			*dpp = NULL;
417191783Srmacklem		} else {
418191783Srmacklem			*dpp = tdp;
419191783Srmacklem		}
420191783Srmacklem	}
421191783Srmacklem	NFSUNLOCKCLSTATE();
422191783Srmacklem	return (0);
423191783Srmacklem}
424191783Srmacklem
425191783Srmacklem/*
426191783Srmacklem * Find a delegation for this file handle. Return NULL upon failure.
427191783Srmacklem */
428191783Srmacklemstatic struct nfscldeleg *
429191783Srmacklemnfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
430191783Srmacklem{
431191783Srmacklem	struct nfscldeleg *dp;
432191783Srmacklem
433191783Srmacklem	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) {
434191783Srmacklem	    if (dp->nfsdl_fhlen == fhlen &&
435191783Srmacklem		!NFSBCMP(dp->nfsdl_fh, fhp, fhlen))
436191783Srmacklem		break;
437191783Srmacklem	}
438191783Srmacklem	return (dp);
439191783Srmacklem}
440191783Srmacklem
441191783Srmacklem/*
442191783Srmacklem * Get a stateid for an I/O operation. First, look for an open and iff
443191783Srmacklem * found, return either a lockowner stateid or the open stateid.
444191783Srmacklem * If no Open is found, just return error and the special stateid of all zeros.
445191783Srmacklem */
446191783SrmacklemAPPLESTATIC int
447191783Srmacklemnfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
448191783Srmacklem    struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp,
449191783Srmacklem    void **lckpp)
450191783Srmacklem{
451191783Srmacklem	struct nfsclclient *clp;
452191783Srmacklem	struct nfsclowner *owp;
453195510Srmacklem	struct nfsclopen *op = NULL;
454191783Srmacklem	struct nfscllockowner *lp;
455191783Srmacklem	struct nfscldeleg *dp;
456191783Srmacklem	struct nfsnode *np;
457191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
458191783Srmacklem	int error, done;
459191783Srmacklem
460191783Srmacklem	*lckpp = NULL;
461191783Srmacklem	/*
462191783Srmacklem	 * Initially, just set the special stateid of all zeros.
463191783Srmacklem	 */
464191783Srmacklem	stateidp->seqid = 0;
465191783Srmacklem	stateidp->other[0] = 0;
466191783Srmacklem	stateidp->other[1] = 0;
467191783Srmacklem	stateidp->other[2] = 0;
468191783Srmacklem	if (vnode_vtype(vp) != VREG)
469191783Srmacklem		return (EISDIR);
470191783Srmacklem	np = VTONFS(vp);
471191783Srmacklem	NFSLOCKCLSTATE();
472191783Srmacklem	clp = nfscl_findcl(VFSTONFS(vnode_mount(vp)));
473191783Srmacklem	if (clp == NULL) {
474191783Srmacklem		NFSUNLOCKCLSTATE();
475191783Srmacklem		return (EACCES);
476191783Srmacklem	}
477191783Srmacklem
478191783Srmacklem	/*
479191783Srmacklem	 * First, look for a delegation.
480191783Srmacklem	 */
481191783Srmacklem	LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
482191783Srmacklem		if (dp->nfsdl_fhlen == fhlen &&
483191783Srmacklem		    !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
484191783Srmacklem			if (!(mode & NFSV4OPEN_ACCESSWRITE) ||
485191783Srmacklem			    (dp->nfsdl_flags & NFSCLDL_WRITE)) {
486191783Srmacklem				stateidp->seqid = dp->nfsdl_stateid.seqid;
487191783Srmacklem				stateidp->other[0] = dp->nfsdl_stateid.other[0];
488191783Srmacklem				stateidp->other[1] = dp->nfsdl_stateid.other[1];
489191783Srmacklem				stateidp->other[2] = dp->nfsdl_stateid.other[2];
490191783Srmacklem				if (!(np->n_flag & NDELEGRECALL)) {
491191783Srmacklem					TAILQ_REMOVE(&clp->nfsc_deleg, dp,
492191783Srmacklem					    nfsdl_list);
493191783Srmacklem					TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
494191783Srmacklem					    nfsdl_list);
495191783Srmacklem					dp->nfsdl_timestamp = NFSD_MONOSEC +
496191783Srmacklem					    120;
497191783Srmacklem					dp->nfsdl_rwlock.nfslock_usecnt++;
498191783Srmacklem					*lckpp = (void *)&dp->nfsdl_rwlock;
499191783Srmacklem				}
500191783Srmacklem				NFSUNLOCKCLSTATE();
501191783Srmacklem				return (0);
502191783Srmacklem			}
503191783Srmacklem			break;
504191783Srmacklem		}
505191783Srmacklem	}
506191783Srmacklem
507191783Srmacklem	if (p != NULL) {
508191783Srmacklem		/*
509191783Srmacklem		 * If p != NULL, we want to search the parentage tree
510191783Srmacklem		 * for a matching OpenOwner and use that.
511191783Srmacklem		 */
512191783Srmacklem		nfscl_filllockowner(p, own);
513191783Srmacklem		error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, NULL, p,
514191783Srmacklem		    mode, NULL, &op);
515195510Srmacklem		if (error == 0) {
516195510Srmacklem			/* now look for a lockowner */
517195510Srmacklem			LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
518195510Srmacklem				if (!NFSBCMP(lp->nfsl_owner, own,
519195510Srmacklem				    NFSV4CL_LOCKNAMELEN)) {
520195510Srmacklem					stateidp->seqid =
521195510Srmacklem					    lp->nfsl_stateid.seqid;
522195510Srmacklem					stateidp->other[0] =
523195510Srmacklem					    lp->nfsl_stateid.other[0];
524195510Srmacklem					stateidp->other[1] =
525195510Srmacklem					    lp->nfsl_stateid.other[1];
526195510Srmacklem					stateidp->other[2] =
527195510Srmacklem					    lp->nfsl_stateid.other[2];
528195510Srmacklem					NFSUNLOCKCLSTATE();
529195510Srmacklem					return (0);
530195510Srmacklem				}
531195510Srmacklem			}
532191783Srmacklem		}
533195510Srmacklem	}
534195510Srmacklem	if (op == NULL) {
535195510Srmacklem		/* If not found, just look for any OpenOwner that will work. */
536191783Srmacklem		done = 0;
537191783Srmacklem		owp = LIST_FIRST(&clp->nfsc_owner);
538191783Srmacklem		while (!done && owp != NULL) {
539195510Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
540195510Srmacklem				if (op->nfso_fhlen == fhlen &&
541195510Srmacklem				    !NFSBCMP(op->nfso_fh, nfhp, fhlen) &&
542195510Srmacklem				    (mode & op->nfso_mode) == mode) {
543195510Srmacklem					done = 1;
544195510Srmacklem					break;
545195510Srmacklem				}
546191783Srmacklem			}
547195510Srmacklem			if (!done)
548195510Srmacklem				owp = LIST_NEXT(owp, nfsow_list);
549191783Srmacklem		}
550191783Srmacklem		if (!done) {
551191783Srmacklem			NFSUNLOCKCLSTATE();
552191783Srmacklem			return (ENOENT);
553191783Srmacklem		}
554191783Srmacklem		/* for read aheads or write behinds, use the open cred */
555191783Srmacklem		newnfs_copycred(&op->nfso_cred, cred);
556191783Srmacklem	}
557191783Srmacklem
558191783Srmacklem	/*
559191783Srmacklem	 * No lock stateid, so return the open stateid.
560191783Srmacklem	 */
561191783Srmacklem	stateidp->seqid = op->nfso_stateid.seqid;
562191783Srmacklem	stateidp->other[0] = op->nfso_stateid.other[0];
563191783Srmacklem	stateidp->other[1] = op->nfso_stateid.other[1];
564191783Srmacklem	stateidp->other[2] = op->nfso_stateid.other[2];
565191783Srmacklem	NFSUNLOCKCLSTATE();
566191783Srmacklem	return (0);
567191783Srmacklem}
568191783Srmacklem
569191783Srmacklem/*
570191783Srmacklem * Get an existing open. Search up the parentage tree for a match and
571191783Srmacklem * return with the first one found.
572191783Srmacklem */
573191783Srmacklemstatic int
574191783Srmacklemnfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen,
575191783Srmacklem    u_int8_t *rown, NFSPROC_T *p, u_int32_t mode, struct nfsclowner **owpp,
576191783Srmacklem    struct nfsclopen **opp)
577191783Srmacklem{
578191783Srmacklem	struct nfsclowner *owp = NULL;
579191783Srmacklem	struct nfsclopen *op;
580191783Srmacklem	NFSPROC_T *nproc;
581191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp;
582191783Srmacklem
583191783Srmacklem	nproc = p;
584191783Srmacklem	op = NULL;
585191783Srmacklem	while (op == NULL && (nproc != NULL || rown != NULL)) {
586191783Srmacklem		if (nproc != NULL) {
587191783Srmacklem			nfscl_filllockowner(nproc, own);
588191783Srmacklem			ownp = own;
589191783Srmacklem		} else {
590191783Srmacklem			ownp = rown;
591191783Srmacklem		}
592191783Srmacklem		/* Search the client list */
593191783Srmacklem		LIST_FOREACH(owp, ohp, nfsow_list) {
594191783Srmacklem			if (!NFSBCMP(owp->nfsow_owner, ownp,
595191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
596191783Srmacklem				break;
597191783Srmacklem		}
598191783Srmacklem		if (owp != NULL) {
599191783Srmacklem			/* and look for the correct open */
600191783Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
601191783Srmacklem				if (op->nfso_fhlen == fhlen &&
602191783Srmacklem				    !NFSBCMP(op->nfso_fh, nfhp, fhlen)
603191783Srmacklem				    && (op->nfso_mode & mode) == mode) {
604191783Srmacklem					break;
605191783Srmacklem				}
606191783Srmacklem			}
607191783Srmacklem		}
608191783Srmacklem		if (rown != NULL)
609191783Srmacklem			break;
610191783Srmacklem		if (op == NULL)
611191783Srmacklem			nproc = nfscl_getparent(nproc);
612191783Srmacklem	}
613191783Srmacklem	if (op == NULL) {
614191783Srmacklem		return (EBADF);
615191783Srmacklem	}
616191783Srmacklem	if (owpp)
617191783Srmacklem		*owpp = owp;
618191783Srmacklem	*opp = op;
619191783Srmacklem	return (0);
620191783Srmacklem}
621191783Srmacklem
622191783Srmacklem/*
623191783Srmacklem * Release use of an open owner. Called when open operations are done
624191783Srmacklem * with the open owner.
625191783Srmacklem */
626191783SrmacklemAPPLESTATIC void
627191783Srmacklemnfscl_ownerrelease(struct nfsclowner *owp, __unused int error,
628191783Srmacklem    __unused int candelete, int unlocked)
629191783Srmacklem{
630191783Srmacklem
631191783Srmacklem	if (owp == NULL)
632191783Srmacklem		return;
633191783Srmacklem	NFSLOCKCLSTATE();
634191783Srmacklem	if (!unlocked)
635191783Srmacklem		nfscl_lockunlock(&owp->nfsow_rwlock);
636191783Srmacklem	nfscl_clrelease(owp->nfsow_clp);
637191783Srmacklem	NFSUNLOCKCLSTATE();
638191783Srmacklem}
639191783Srmacklem
640191783Srmacklem/*
641191783Srmacklem * Release use of an open structure under an open owner.
642191783Srmacklem */
643191783SrmacklemAPPLESTATIC void
644191783Srmacklemnfscl_openrelease(struct nfsclopen *op, int error, int candelete)
645191783Srmacklem{
646191783Srmacklem	struct nfsclclient *clp;
647191783Srmacklem	struct nfsclowner *owp;
648191783Srmacklem
649191783Srmacklem	if (op == NULL)
650191783Srmacklem		return;
651191783Srmacklem	NFSLOCKCLSTATE();
652191783Srmacklem	owp = op->nfso_own;
653191783Srmacklem	nfscl_lockunlock(&owp->nfsow_rwlock);
654191783Srmacklem	clp = owp->nfsow_clp;
655191783Srmacklem	if (error && candelete && op->nfso_opencnt == 0)
656191783Srmacklem		nfscl_freeopen(op, 0);
657191783Srmacklem	nfscl_clrelease(clp);
658191783Srmacklem	NFSUNLOCKCLSTATE();
659191783Srmacklem}
660191783Srmacklem
661191783Srmacklem/*
662191783Srmacklem * Called to get a clientid structure. It will optionally lock the
663191783Srmacklem * client data structures to do the SetClientId/SetClientId_confirm,
664191783Srmacklem * but will release that lock and return the clientid with a refernce
665191783Srmacklem * count on it.
666193735Srmacklem * If the "cred" argument is NULL, a new clientid should not be created.
667193735Srmacklem * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot
668193735Srmacklem * be done.
669192337Srmacklem * It always clpp with a reference count on it, unless returning an error.
670191783Srmacklem */
671191783SrmacklemAPPLESTATIC int
672191783Srmacklemnfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
673191783Srmacklem    struct nfsclclient **clpp)
674191783Srmacklem{
675191783Srmacklem	struct nfsclclient *clp;
676193735Srmacklem	struct nfsclclient *newclp = NULL;
677191783Srmacklem	struct nfscllockowner *lp, *nlp;
678191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
679193066Sjamie	char uuid[HOSTUUIDLEN];
680191783Srmacklem	int igotlock = 0, error, trystalecnt, clidinusedelay, i;
681193735Srmacklem	u_int16_t idlen = 0;
682191783Srmacklem
683193735Srmacklem	if (cred != NULL) {
684194117Sjamie		getcredhostuuid(cred, uuid, sizeof uuid);
685193735Srmacklem		idlen = strlen(uuid);
686193735Srmacklem		if (idlen > 0)
687193735Srmacklem			idlen += sizeof (u_int64_t);
688193735Srmacklem		else
689193735Srmacklem			idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
690193735Srmacklem		MALLOC(newclp, struct nfsclclient *,
691193735Srmacklem		    sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
692193735Srmacklem		    M_WAITOK);
693193735Srmacklem	}
694191783Srmacklem	NFSLOCKCLSTATE();
695191783Srmacklem	clp = nmp->nm_clp;
696191783Srmacklem	if (clp == NULL) {
697193735Srmacklem		if (newclp == NULL) {
698193735Srmacklem			NFSUNLOCKCLSTATE();
699193735Srmacklem			return (EACCES);
700193735Srmacklem		}
701191783Srmacklem		clp = newclp;
702191783Srmacklem		NFSBZERO((caddr_t)clp, sizeof(struct nfsclclient) + idlen - 1);
703191783Srmacklem		clp->nfsc_idlen = idlen;
704191783Srmacklem		LIST_INIT(&clp->nfsc_owner);
705191783Srmacklem		TAILQ_INIT(&clp->nfsc_deleg);
706191783Srmacklem		for (i = 0; i < NFSCLDELEGHASHSIZE; i++)
707191783Srmacklem			LIST_INIT(&clp->nfsc_deleghash[i]);
708191783Srmacklem		LIST_INIT(&clp->nfsc_defunctlockowner);
709191783Srmacklem		clp->nfsc_flags = NFSCLFLAGS_INITED;
710191783Srmacklem		clp->nfsc_clientidrev = 1;
711191783Srmacklem		clp->nfsc_cbident = nfscl_nextcbident();
712193066Sjamie		nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id,
713191783Srmacklem		    clp->nfsc_idlen);
714191783Srmacklem		LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list);
715191783Srmacklem		nmp->nm_clp = clp;
716191783Srmacklem		clp->nfsc_nmp = nmp;
717191783Srmacklem		NFSUNLOCKCLSTATE();
718191783Srmacklem		nfscl_start_renewthread(clp);
719191783Srmacklem	} else {
720191783Srmacklem		NFSUNLOCKCLSTATE();
721193735Srmacklem		if (newclp != NULL)
722193735Srmacklem			FREE((caddr_t)newclp, M_NFSCLCLIENT);
723191783Srmacklem	}
724191783Srmacklem	NFSLOCKCLSTATE();
725191783Srmacklem	while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock)
726191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
727191783Srmacklem		    NFSCLSTATEMUTEXPTR);
728191783Srmacklem	if (!igotlock)
729191783Srmacklem		nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR);
730191783Srmacklem	NFSUNLOCKCLSTATE();
731191783Srmacklem
732191783Srmacklem	/*
733191783Srmacklem	 * If it needs a clientid, do the setclientid now.
734191783Srmacklem	 */
735191783Srmacklem	if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) {
736191783Srmacklem		if (!igotlock)
737191783Srmacklem			panic("nfscl_clget");
738193735Srmacklem		if (p == NULL || cred == NULL) {
739191783Srmacklem			NFSLOCKCLSTATE();
740191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
741191783Srmacklem			NFSUNLOCKCLSTATE();
742191783Srmacklem			return (EACCES);
743191783Srmacklem		}
744191783Srmacklem		/* get rid of defunct lockowners */
745191783Srmacklem		LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list,
746191783Srmacklem		    nlp) {
747191783Srmacklem			nfscl_freelockowner(lp, 0);
748191783Srmacklem		}
749191783Srmacklem		/*
750191783Srmacklem		 * If RFC3530 Sec. 14.2.33 is taken literally,
751191783Srmacklem		 * NFSERR_CLIDINUSE will be returned persistently for the
752191783Srmacklem		 * case where a new mount of the same file system is using
753191783Srmacklem		 * a different principal. In practice, NFSERR_CLIDINUSE is
754191783Srmacklem		 * only returned when there is outstanding unexpired state
755191783Srmacklem		 * on the clientid. As such, try for twice the lease
756191783Srmacklem		 * interval, if we know what that is. Otherwise, make a
757191783Srmacklem		 * wild ass guess.
758191783Srmacklem		 * The case of returning NFSERR_STALECLIENTID is far less
759191783Srmacklem		 * likely, but might occur if there is a significant delay
760191783Srmacklem		 * between doing the SetClientID and SetClientIDConfirm Ops,
761191783Srmacklem		 * such that the server throws away the clientid before
762191783Srmacklem		 * receiving the SetClientIDConfirm.
763191783Srmacklem		 */
764191783Srmacklem		if (clp->nfsc_renew > 0)
765191783Srmacklem			clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
766191783Srmacklem		else
767191783Srmacklem			clidinusedelay = 120;
768191783Srmacklem		trystalecnt = 3;
769191783Srmacklem		do {
770193735Srmacklem			error = nfsrpc_setclient(VFSTONFS(vnode_mount(vp)),
771193735Srmacklem			    clp, cred, p);
772191783Srmacklem			if (error == NFSERR_STALECLIENTID ||
773191783Srmacklem			    error == NFSERR_STALEDONTRECOVER ||
774191783Srmacklem			    error == NFSERR_CLIDINUSE) {
775191783Srmacklem				(void) nfs_catnap(PZERO, "nfs_setcl");
776191783Srmacklem			}
777191783Srmacklem		} while (((error == NFSERR_STALECLIENTID ||
778191783Srmacklem		     error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) ||
779191783Srmacklem		    (error == NFSERR_CLIDINUSE && --clidinusedelay > 0));
780191783Srmacklem		if (error) {
781191783Srmacklem			NFSLOCKCLSTATE();
782191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
783191783Srmacklem			NFSUNLOCKCLSTATE();
784191783Srmacklem			return (error);
785191783Srmacklem		}
786191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
787191783Srmacklem	}
788191783Srmacklem	if (igotlock) {
789191783Srmacklem		NFSLOCKCLSTATE();
790191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 1);
791191783Srmacklem		NFSUNLOCKCLSTATE();
792191783Srmacklem	}
793191783Srmacklem
794191783Srmacklem	*clpp = clp;
795191783Srmacklem	return (0);
796191783Srmacklem}
797191783Srmacklem
798191783Srmacklem/*
799191783Srmacklem * Get a reference to a clientid and return it, if valid.
800191783Srmacklem */
801191783SrmacklemAPPLESTATIC struct nfsclclient *
802191783Srmacklemnfscl_findcl(struct nfsmount *nmp)
803191783Srmacklem{
804191783Srmacklem	struct nfsclclient *clp;
805191783Srmacklem
806191783Srmacklem	clp = nmp->nm_clp;
807191783Srmacklem	if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID))
808191783Srmacklem		return (NULL);
809191783Srmacklem	return (clp);
810191783Srmacklem}
811191783Srmacklem
812191783Srmacklem/*
813191783Srmacklem * Release the clientid structure. It may be locked or reference counted.
814191783Srmacklem */
815191783Srmacklemstatic void
816191783Srmacklemnfscl_clrelease(struct nfsclclient *clp)
817191783Srmacklem{
818191783Srmacklem
819191783Srmacklem	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
820191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
821191783Srmacklem	else
822191783Srmacklem		nfsv4_relref(&clp->nfsc_lock);
823191783Srmacklem}
824191783Srmacklem
825191783Srmacklem/*
826191783Srmacklem * External call for nfscl_clrelease.
827191783Srmacklem */
828191783SrmacklemAPPLESTATIC void
829191783Srmacklemnfscl_clientrelease(struct nfsclclient *clp)
830191783Srmacklem{
831191783Srmacklem
832191783Srmacklem	NFSLOCKCLSTATE();
833191783Srmacklem	if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
834191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
835191783Srmacklem	else
836191783Srmacklem		nfsv4_relref(&clp->nfsc_lock);
837191783Srmacklem	NFSUNLOCKCLSTATE();
838191783Srmacklem}
839191783Srmacklem
840191783Srmacklem/*
841191783Srmacklem * Called when wanting to lock a byte region.
842191783Srmacklem */
843191783SrmacklemAPPLESTATIC int
844191783Srmacklemnfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
845191783Srmacklem    short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp,
846191783Srmacklem    int recovery, u_int8_t *rownp, u_int8_t *ropenownp,
847191783Srmacklem    struct nfscllockowner **lpp, int *newonep, int *donelocallyp)
848191783Srmacklem{
849191783Srmacklem	struct nfscllockowner *lp;
850191783Srmacklem	struct nfsclopen *op;
851191783Srmacklem	struct nfsclclient *clp;
852191783Srmacklem	struct nfscllockowner *nlp;
853191783Srmacklem	struct nfscllock *nlop, *otherlop;
854191783Srmacklem	struct nfscldeleg *dp = NULL, *ldp = NULL;
855191783Srmacklem	struct nfscllockownerhead *lhp = NULL;
856191783Srmacklem	struct nfsnode *np;
857191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp;
858191783Srmacklem	int error = 0, ret, donelocally = 0;
859191783Srmacklem	u_int32_t mode;
860191783Srmacklem
861191783Srmacklem	if (type == F_WRLCK)
862191783Srmacklem		mode = NFSV4OPEN_ACCESSWRITE;
863191783Srmacklem	else
864191783Srmacklem		mode = NFSV4OPEN_ACCESSREAD;
865191783Srmacklem	np = VTONFS(vp);
866191783Srmacklem	*lpp = NULL;
867191783Srmacklem	*newonep = 0;
868191783Srmacklem	*donelocallyp = 0;
869191783Srmacklem
870191783Srmacklem	/*
871191783Srmacklem	 * Might need these, so MALLOC them now, to
872191783Srmacklem	 * avoid a tsleep() in MALLOC later.
873191783Srmacklem	 */
874191783Srmacklem	MALLOC(nlp, struct nfscllockowner *,
875191783Srmacklem	    sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK);
876191783Srmacklem	MALLOC(otherlop, struct nfscllock *,
877191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
878191783Srmacklem	MALLOC(nlop, struct nfscllock *,
879191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
880191783Srmacklem	nlop->nfslo_type = type;
881191783Srmacklem	nlop->nfslo_first = off;
882191783Srmacklem	if (len == NFS64BITSSET) {
883191783Srmacklem		nlop->nfslo_end = NFS64BITSSET;
884191783Srmacklem	} else {
885191783Srmacklem		nlop->nfslo_end = off + len;
886191783Srmacklem		if (nlop->nfslo_end <= nlop->nfslo_first)
887191783Srmacklem			error = NFSERR_INVAL;
888191783Srmacklem	}
889191783Srmacklem
890191783Srmacklem	if (!error) {
891191783Srmacklem		if (recovery)
892191783Srmacklem			clp = rclp;
893191783Srmacklem		else
894191783Srmacklem			error = nfscl_getcl(vp, cred, p, &clp);
895191783Srmacklem	}
896191783Srmacklem	if (error) {
897191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
898191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
899191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
900191783Srmacklem		return (error);
901191783Srmacklem	}
902191783Srmacklem
903191783Srmacklem	op = NULL;
904191783Srmacklem	if (recovery) {
905191783Srmacklem		ownp = rownp;
906191783Srmacklem	} else {
907191783Srmacklem		nfscl_filllockowner(p, own);
908191783Srmacklem		ownp = own;
909191783Srmacklem	}
910191783Srmacklem	if (!recovery) {
911191783Srmacklem		NFSLOCKCLSTATE();
912191783Srmacklem		/*
913191783Srmacklem		 * First, search for a delegation. If one exists for this file,
914191783Srmacklem		 * the lock can be done locally against it, so long as there
915191783Srmacklem		 * isn't a local lock conflict.
916191783Srmacklem		 */
917191783Srmacklem		ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
918191783Srmacklem		    np->n_fhp->nfh_len);
919191783Srmacklem		/* Just sanity check for correct type of delegation */
920191783Srmacklem		if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_RECALL) ||
921191783Srmacklem		    (type == F_WRLCK && !(dp->nfsdl_flags & NFSCLDL_WRITE))))
922191783Srmacklem			dp = NULL;
923191783Srmacklem	}
924191783Srmacklem	if (dp != NULL) {
925191783Srmacklem		/* Now, find the associated open to get the correct openowner */
926191783Srmacklem		ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh,
927191783Srmacklem		    np->n_fhp->nfh_len, NULL, p, mode, NULL, &op);
928191783Srmacklem		if (ret)
929191783Srmacklem			ret = nfscl_getopen(&clp->nfsc_owner,
930191783Srmacklem			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p,
931191783Srmacklem			    mode, NULL, &op);
932191783Srmacklem		if (!ret) {
933191783Srmacklem			lhp = &dp->nfsdl_lock;
934191783Srmacklem			TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
935191783Srmacklem			TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
936191783Srmacklem			dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
937191783Srmacklem			donelocally = 1;
938191783Srmacklem		} else {
939191783Srmacklem			dp = NULL;
940191783Srmacklem		}
941191783Srmacklem	}
942191783Srmacklem	if (!donelocally) {
943191783Srmacklem		/*
944191783Srmacklem		 * Get the related Open.
945191783Srmacklem		 */
946191783Srmacklem		if (recovery)
947191783Srmacklem			error = nfscl_getopen(&clp->nfsc_owner,
948191783Srmacklem			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, ropenownp,
949191783Srmacklem			    NULL, mode, NULL, &op);
950191783Srmacklem		else
951191783Srmacklem			error = nfscl_getopen(&clp->nfsc_owner,
952191783Srmacklem			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p,
953191783Srmacklem			    mode, NULL, &op);
954191783Srmacklem		if (!error)
955191783Srmacklem			lhp = &op->nfso_lock;
956191783Srmacklem	}
957191783Srmacklem	if (!error && !recovery)
958201439Srmacklem		error = nfscl_localconflict(clp, np->n_fhp->nfh_fh,
959201439Srmacklem		    np->n_fhp->nfh_len, nlop, ownp, ldp, NULL);
960191783Srmacklem	if (error) {
961191783Srmacklem		if (!recovery) {
962191783Srmacklem			nfscl_clrelease(clp);
963191783Srmacklem			NFSUNLOCKCLSTATE();
964191783Srmacklem		}
965191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
966191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
967191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
968191783Srmacklem		return (error);
969191783Srmacklem	}
970191783Srmacklem
971191783Srmacklem	/*
972191783Srmacklem	 * Ok, see if a lockowner exists and create one, as required.
973191783Srmacklem	 */
974191783Srmacklem	LIST_FOREACH(lp, lhp, nfsl_list) {
975191783Srmacklem		if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
976191783Srmacklem			break;
977191783Srmacklem	}
978191783Srmacklem	if (lp == NULL) {
979191783Srmacklem		NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
980191783Srmacklem		if (recovery)
981191783Srmacklem			NFSBCOPY(ropenownp, nlp->nfsl_openowner,
982191783Srmacklem			    NFSV4CL_LOCKNAMELEN);
983191783Srmacklem		else
984191783Srmacklem			NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner,
985191783Srmacklem			    NFSV4CL_LOCKNAMELEN);
986191783Srmacklem		nlp->nfsl_seqid = 0;
987191783Srmacklem		nlp->nfsl_defunct = 0;
988191783Srmacklem		nlp->nfsl_inprog = NULL;
989191783Srmacklem		nfscl_lockinit(&nlp->nfsl_rwlock);
990191783Srmacklem		LIST_INIT(&nlp->nfsl_lock);
991191783Srmacklem		if (donelocally) {
992191783Srmacklem			nlp->nfsl_open = NULL;
993191783Srmacklem			newnfsstats.cllocallockowners++;
994191783Srmacklem		} else {
995191783Srmacklem			nlp->nfsl_open = op;
996191783Srmacklem			newnfsstats.cllockowners++;
997191783Srmacklem		}
998191783Srmacklem		LIST_INSERT_HEAD(lhp, nlp, nfsl_list);
999191783Srmacklem		lp = nlp;
1000191783Srmacklem		nlp = NULL;
1001191783Srmacklem		*newonep = 1;
1002191783Srmacklem	}
1003191783Srmacklem
1004191783Srmacklem	/*
1005191783Srmacklem	 * Now, update the byte ranges for locks.
1006191783Srmacklem	 */
1007191783Srmacklem	ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally);
1008191783Srmacklem	if (!ret)
1009191783Srmacklem		donelocally = 1;
1010191783Srmacklem	if (donelocally) {
1011191783Srmacklem		*donelocallyp = 1;
1012191783Srmacklem		if (!recovery)
1013191783Srmacklem			nfscl_clrelease(clp);
1014191783Srmacklem	} else {
1015191783Srmacklem		/*
1016191783Srmacklem		 * Serial modifications on the lock owner for multiple threads
1017191783Srmacklem		 * for the same process using a read/write lock.
1018191783Srmacklem		 */
1019191783Srmacklem		if (!recovery)
1020191783Srmacklem			nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1021191783Srmacklem	}
1022191783Srmacklem	if (!recovery)
1023191783Srmacklem		NFSUNLOCKCLSTATE();
1024191783Srmacklem
1025191783Srmacklem	if (nlp)
1026191783Srmacklem		FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1027191783Srmacklem	if (nlop)
1028191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
1029191783Srmacklem	if (otherlop)
1030191783Srmacklem		FREE((caddr_t)otherlop, M_NFSCLLOCK);
1031191783Srmacklem
1032191783Srmacklem	*lpp = lp;
1033191783Srmacklem	return (0);
1034191783Srmacklem}
1035191783Srmacklem
1036191783Srmacklem/*
1037191783Srmacklem * Called to unlock a byte range, for LockU.
1038191783Srmacklem */
1039191783SrmacklemAPPLESTATIC int
1040191783Srmacklemnfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
1041191783Srmacklem    __unused struct ucred *cred, NFSPROC_T *p, int callcnt,
1042191783Srmacklem    struct nfsclclient *clp, struct nfscllockowner **lpp, int *dorpcp)
1043191783Srmacklem{
1044191783Srmacklem	struct nfscllockowner *lp;
1045191783Srmacklem	struct nfsclowner *owp;
1046191783Srmacklem	struct nfsclopen *op;
1047191783Srmacklem	struct nfscllock *nlop, *other_lop = NULL;
1048191783Srmacklem	struct nfscldeleg *dp;
1049191783Srmacklem	struct nfsnode *np;
1050191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1051201439Srmacklem	int ret = 0, fnd;
1052191783Srmacklem
1053191783Srmacklem	np = VTONFS(vp);
1054191783Srmacklem	*lpp = NULL;
1055191783Srmacklem	*dorpcp = 0;
1056191783Srmacklem
1057191783Srmacklem	/*
1058191783Srmacklem	 * Might need these, so MALLOC them now, to
1059191783Srmacklem	 * avoid a tsleep() in MALLOC later.
1060191783Srmacklem	 */
1061191783Srmacklem	MALLOC(nlop, struct nfscllock *,
1062191783Srmacklem	    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1063191783Srmacklem	nlop->nfslo_type = F_UNLCK;
1064191783Srmacklem	nlop->nfslo_first = off;
1065191783Srmacklem	if (len == NFS64BITSSET) {
1066191783Srmacklem		nlop->nfslo_end = NFS64BITSSET;
1067191783Srmacklem	} else {
1068191783Srmacklem		nlop->nfslo_end = off + len;
1069191783Srmacklem		if (nlop->nfslo_end <= nlop->nfslo_first) {
1070191783Srmacklem			FREE((caddr_t)nlop, M_NFSCLLOCK);
1071191783Srmacklem			return (NFSERR_INVAL);
1072191783Srmacklem		}
1073191783Srmacklem	}
1074191783Srmacklem	if (callcnt == 0) {
1075191783Srmacklem		MALLOC(other_lop, struct nfscllock *,
1076191783Srmacklem		    sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1077191783Srmacklem		*other_lop = *nlop;
1078191783Srmacklem	}
1079191783Srmacklem	nfscl_filllockowner(p, own);
1080191783Srmacklem	dp = NULL;
1081191783Srmacklem	NFSLOCKCLSTATE();
1082191783Srmacklem	if (callcnt == 0)
1083191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1084191783Srmacklem		    np->n_fhp->nfh_len);
1085191783Srmacklem
1086191783Srmacklem	/*
1087191783Srmacklem	 * First, unlock any local regions on a delegation.
1088191783Srmacklem	 */
1089191783Srmacklem	if (dp != NULL) {
1090191783Srmacklem		/* Look for this lockowner. */
1091191783Srmacklem		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1092191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1093191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1094191783Srmacklem				break;
1095191783Srmacklem		}
1096191783Srmacklem		if (lp != NULL)
1097191783Srmacklem			/* Use other_lop, so nlop is still available */
1098191783Srmacklem			(void)nfscl_updatelock(lp, &other_lop, NULL, 1);
1099191783Srmacklem	}
1100191783Srmacklem
1101191783Srmacklem	/*
1102191783Srmacklem	 * Now, find a matching open/lockowner that hasn't already been done,
1103191783Srmacklem	 * as marked by nfsl_inprog.
1104191783Srmacklem	 */
1105191783Srmacklem	lp = NULL;
1106191783Srmacklem	fnd = 0;
1107191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1108191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1109191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1110191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1111191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1112191783Srmacklem			if (lp->nfsl_inprog == NULL &&
1113191783Srmacklem			    !NFSBCMP(lp->nfsl_owner, own,
1114191783Srmacklem			     NFSV4CL_LOCKNAMELEN)) {
1115191783Srmacklem				fnd = 1;
1116191783Srmacklem				break;
1117191783Srmacklem			}
1118191783Srmacklem		    }
1119191783Srmacklem		    if (fnd)
1120191783Srmacklem			break;
1121191783Srmacklem		}
1122191783Srmacklem	    }
1123191783Srmacklem	    if (fnd)
1124191783Srmacklem		break;
1125191783Srmacklem	}
1126191783Srmacklem
1127191783Srmacklem	if (lp != NULL) {
1128191783Srmacklem		ret = nfscl_updatelock(lp, &nlop, NULL, 0);
1129191783Srmacklem		if (ret)
1130191783Srmacklem			*dorpcp = 1;
1131191783Srmacklem		/*
1132191783Srmacklem		 * Serial modifications on the lock owner for multiple
1133191783Srmacklem		 * threads for the same process using a read/write lock.
1134191783Srmacklem		 */
1135191783Srmacklem		lp->nfsl_inprog = p;
1136191783Srmacklem		nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1137191783Srmacklem		*lpp = lp;
1138191783Srmacklem	}
1139191783Srmacklem	NFSUNLOCKCLSTATE();
1140191783Srmacklem	if (nlop)
1141191783Srmacklem		FREE((caddr_t)nlop, M_NFSCLLOCK);
1142191783Srmacklem	if (other_lop)
1143191783Srmacklem		FREE((caddr_t)other_lop, M_NFSCLLOCK);
1144191783Srmacklem	return (0);
1145191783Srmacklem}
1146191783Srmacklem
1147191783Srmacklem/*
1148191783Srmacklem * Release all lockowners marked in progess for this process and file.
1149191783Srmacklem */
1150191783SrmacklemAPPLESTATIC void
1151191783Srmacklemnfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p)
1152191783Srmacklem{
1153191783Srmacklem	struct nfsclowner *owp;
1154191783Srmacklem	struct nfsclopen *op;
1155191783Srmacklem	struct nfscllockowner *lp;
1156191783Srmacklem	struct nfsnode *np;
1157191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1158191783Srmacklem
1159191783Srmacklem	np = VTONFS(vp);
1160191783Srmacklem	nfscl_filllockowner(p, own);
1161191783Srmacklem	NFSLOCKCLSTATE();
1162191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1163191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1164191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1165191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1166191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1167191783Srmacklem			if (lp->nfsl_inprog == p &&
1168191783Srmacklem			    !NFSBCMP(lp->nfsl_owner, own,
1169191783Srmacklem			    NFSV4CL_LOCKNAMELEN)) {
1170191783Srmacklem			    lp->nfsl_inprog = NULL;
1171191783Srmacklem			    nfscl_lockunlock(&lp->nfsl_rwlock);
1172191783Srmacklem			}
1173191783Srmacklem		    }
1174191783Srmacklem		}
1175191783Srmacklem	    }
1176191783Srmacklem	}
1177191783Srmacklem	nfscl_clrelease(clp);
1178191783Srmacklem	NFSUNLOCKCLSTATE();
1179191783Srmacklem}
1180191783Srmacklem
1181191783Srmacklem/*
1182191783Srmacklem * Called to find out if any bytes within the byte range specified are
1183191783Srmacklem * write locked by the calling process. Used to determine if flushing
1184191783Srmacklem * is required before a LockU.
1185191783Srmacklem * If in doubt, return 1, so the flush will occur.
1186191783Srmacklem */
1187191783SrmacklemAPPLESTATIC int
1188191783Srmacklemnfscl_checkwritelocked(vnode_t vp, struct flock *fl,
1189191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
1190191783Srmacklem{
1191191783Srmacklem	struct nfsclowner *owp;
1192191783Srmacklem	struct nfscllockowner *lp;
1193191783Srmacklem	struct nfsclopen *op;
1194191783Srmacklem	struct nfsclclient *clp;
1195191783Srmacklem	struct nfscllock *lop;
1196191783Srmacklem	struct nfscldeleg *dp;
1197191783Srmacklem	struct nfsnode *np;
1198191783Srmacklem	u_int64_t off, end;
1199191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1200191783Srmacklem	int error = 0;
1201191783Srmacklem
1202191783Srmacklem	np = VTONFS(vp);
1203191783Srmacklem	switch (fl->l_whence) {
1204191783Srmacklem	case SEEK_SET:
1205191783Srmacklem	case SEEK_CUR:
1206191783Srmacklem		/*
1207191783Srmacklem		 * Caller is responsible for adding any necessary offset
1208191783Srmacklem		 * when SEEK_CUR is used.
1209191783Srmacklem		 */
1210191783Srmacklem		off = fl->l_start;
1211191783Srmacklem		break;
1212191783Srmacklem	case SEEK_END:
1213191783Srmacklem		off = np->n_size + fl->l_start;
1214191783Srmacklem		break;
1215191783Srmacklem	default:
1216191783Srmacklem		return (1);
1217191783Srmacklem	};
1218191783Srmacklem	if (fl->l_len != 0) {
1219191783Srmacklem		end = off + fl->l_len;
1220191783Srmacklem		if (end < off)
1221191783Srmacklem			return (1);
1222191783Srmacklem	} else {
1223191783Srmacklem		end = NFS64BITSSET;
1224191783Srmacklem	}
1225191783Srmacklem
1226191783Srmacklem	error = nfscl_getcl(vp, cred, p, &clp);
1227191783Srmacklem	if (error)
1228191783Srmacklem		return (1);
1229191783Srmacklem	nfscl_filllockowner(p, own);
1230191783Srmacklem	NFSLOCKCLSTATE();
1231191783Srmacklem
1232191783Srmacklem	/*
1233191783Srmacklem	 * First check the delegation locks.
1234191783Srmacklem	 */
1235191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
1236191783Srmacklem	if (dp != NULL) {
1237191783Srmacklem		LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1238191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1239191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1240191783Srmacklem				break;
1241191783Srmacklem		}
1242191783Srmacklem		if (lp != NULL) {
1243191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1244191783Srmacklem				if (lop->nfslo_first >= end)
1245191783Srmacklem					break;
1246191783Srmacklem				if (lop->nfslo_end <= off)
1247191783Srmacklem					continue;
1248191783Srmacklem				if (lop->nfslo_type == F_WRLCK) {
1249191783Srmacklem					nfscl_clrelease(clp);
1250191783Srmacklem					NFSUNLOCKCLSTATE();
1251191783Srmacklem					return (1);
1252191783Srmacklem				}
1253191783Srmacklem			}
1254191783Srmacklem		}
1255191783Srmacklem	}
1256191783Srmacklem
1257191783Srmacklem	/*
1258191783Srmacklem	 * Now, check state against the server.
1259191783Srmacklem	 */
1260191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1261191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1262191783Srmacklem		if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1263191783Srmacklem		    !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1264191783Srmacklem		    LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1265191783Srmacklem			if (!NFSBCMP(lp->nfsl_owner, own,
1266191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1267191783Srmacklem			    break;
1268191783Srmacklem		    }
1269191783Srmacklem		    if (lp != NULL) {
1270191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1271191783Srmacklem			    if (lop->nfslo_first >= end)
1272191783Srmacklem				break;
1273191783Srmacklem			    if (lop->nfslo_end <= off)
1274191783Srmacklem				continue;
1275191783Srmacklem			    if (lop->nfslo_type == F_WRLCK) {
1276191783Srmacklem				nfscl_clrelease(clp);
1277191783Srmacklem				NFSUNLOCKCLSTATE();
1278191783Srmacklem				return (1);
1279191783Srmacklem			    }
1280191783Srmacklem			}
1281191783Srmacklem		    }
1282191783Srmacklem		}
1283191783Srmacklem	    }
1284191783Srmacklem	}
1285191783Srmacklem	nfscl_clrelease(clp);
1286191783Srmacklem	NFSUNLOCKCLSTATE();
1287191783Srmacklem	return (0);
1288191783Srmacklem}
1289191783Srmacklem
1290191783Srmacklem/*
1291191783Srmacklem * Release a byte range lock owner structure.
1292191783Srmacklem */
1293191783SrmacklemAPPLESTATIC void
1294191783Srmacklemnfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete)
1295191783Srmacklem{
1296191783Srmacklem	struct nfsclclient *clp;
1297191783Srmacklem
1298191783Srmacklem	if (lp == NULL)
1299191783Srmacklem		return;
1300191783Srmacklem	NFSLOCKCLSTATE();
1301191783Srmacklem	clp = lp->nfsl_open->nfso_own->nfsow_clp;
1302191783Srmacklem	if (error != 0 && candelete &&
1303191783Srmacklem	    (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0)
1304191783Srmacklem		nfscl_freelockowner(lp, 0);
1305191783Srmacklem	else
1306191783Srmacklem		nfscl_lockunlock(&lp->nfsl_rwlock);
1307191783Srmacklem	nfscl_clrelease(clp);
1308191783Srmacklem	NFSUNLOCKCLSTATE();
1309191783Srmacklem}
1310191783Srmacklem
1311191783Srmacklem/*
1312191783Srmacklem * Free up an open structure and any associated byte range lock structures.
1313191783Srmacklem */
1314191783SrmacklemAPPLESTATIC void
1315191783Srmacklemnfscl_freeopen(struct nfsclopen *op, int local)
1316191783Srmacklem{
1317191783Srmacklem
1318191783Srmacklem	LIST_REMOVE(op, nfso_list);
1319191783Srmacklem	nfscl_freealllocks(&op->nfso_lock, local);
1320191783Srmacklem	FREE((caddr_t)op, M_NFSCLOPEN);
1321191783Srmacklem	if (local)
1322191783Srmacklem		newnfsstats.cllocalopens--;
1323191783Srmacklem	else
1324191783Srmacklem		newnfsstats.clopens--;
1325191783Srmacklem}
1326191783Srmacklem
1327191783Srmacklem/*
1328191783Srmacklem * Free up all lock owners and associated locks.
1329191783Srmacklem */
1330191783Srmacklemstatic void
1331191783Srmacklemnfscl_freealllocks(struct nfscllockownerhead *lhp, int local)
1332191783Srmacklem{
1333191783Srmacklem	struct nfscllockowner *lp, *nlp;
1334191783Srmacklem
1335191783Srmacklem	LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) {
1336191783Srmacklem		if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1337191783Srmacklem			panic("nfscllckw");
1338191783Srmacklem		nfscl_freelockowner(lp, local);
1339191783Srmacklem	}
1340191783Srmacklem}
1341191783Srmacklem
1342191783Srmacklem/*
1343191783Srmacklem * Called for an Open when NFSERR_EXPIRED is received from the server.
1344191783Srmacklem * If there are no byte range locks nor a Share Deny lost, try to do a
1345191783Srmacklem * fresh Open. Otherwise, free the open.
1346191783Srmacklem */
1347191783Srmacklemstatic int
1348191783Srmacklemnfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op,
1349191783Srmacklem    struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
1350191783Srmacklem{
1351191783Srmacklem	struct nfscllockowner *lp;
1352191783Srmacklem	struct nfscldeleg *dp;
1353191783Srmacklem	int mustdelete = 0, error;
1354191783Srmacklem
1355191783Srmacklem	/*
1356191783Srmacklem	 * Look for any byte range lock(s).
1357191783Srmacklem	 */
1358191783Srmacklem	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1359191783Srmacklem		if (!LIST_EMPTY(&lp->nfsl_lock)) {
1360191783Srmacklem			mustdelete = 1;
1361191783Srmacklem			break;
1362191783Srmacklem		}
1363191783Srmacklem	}
1364191783Srmacklem
1365191783Srmacklem	/*
1366191783Srmacklem	 * If no byte range lock(s) nor a Share deny, try to re-open.
1367191783Srmacklem	 */
1368191783Srmacklem	if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) {
1369191783Srmacklem		newnfs_copycred(&op->nfso_cred, cred);
1370191783Srmacklem		dp = NULL;
1371191783Srmacklem		error = nfsrpc_reopen(nmp, op->nfso_fh,
1372191783Srmacklem		    op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p);
1373191783Srmacklem		if (error) {
1374191783Srmacklem			mustdelete = 1;
1375191783Srmacklem			if (dp != NULL) {
1376191783Srmacklem				FREE((caddr_t)dp, M_NFSCLDELEG);
1377191783Srmacklem				dp = NULL;
1378191783Srmacklem			}
1379191783Srmacklem		}
1380191783Srmacklem		if (dp != NULL)
1381191783Srmacklem			nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh,
1382191783Srmacklem			    op->nfso_fhlen, cred, p, &dp);
1383191783Srmacklem	}
1384191783Srmacklem
1385191783Srmacklem	/*
1386191783Srmacklem	 * If a byte range lock or Share deny or couldn't re-open, free it.
1387191783Srmacklem	 */
1388191783Srmacklem	if (mustdelete)
1389191783Srmacklem		nfscl_freeopen(op, 0);
1390191783Srmacklem	return (mustdelete);
1391191783Srmacklem}
1392191783Srmacklem
1393191783Srmacklem/*
1394191783Srmacklem * Free up an open owner structure.
1395191783Srmacklem */
1396191783Srmacklemstatic void
1397191783Srmacklemnfscl_freeopenowner(struct nfsclowner *owp, int local)
1398191783Srmacklem{
1399191783Srmacklem
1400191783Srmacklem	LIST_REMOVE(owp, nfsow_list);
1401191783Srmacklem	FREE((caddr_t)owp, M_NFSCLOWNER);
1402191783Srmacklem	if (local)
1403191783Srmacklem		newnfsstats.cllocalopenowners--;
1404191783Srmacklem	else
1405191783Srmacklem		newnfsstats.clopenowners--;
1406191783Srmacklem}
1407191783Srmacklem
1408191783Srmacklem/*
1409191783Srmacklem * Free up a byte range lock owner structure.
1410191783Srmacklem */
1411191783Srmacklemstatic void
1412191783Srmacklemnfscl_freelockowner(struct nfscllockowner *lp, int local)
1413191783Srmacklem{
1414191783Srmacklem	struct nfscllock *lop, *nlop;
1415191783Srmacklem
1416191783Srmacklem	LIST_REMOVE(lp, nfsl_list);
1417191783Srmacklem	LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
1418191783Srmacklem		nfscl_freelock(lop, local);
1419191783Srmacklem	}
1420191783Srmacklem	FREE((caddr_t)lp, M_NFSCLLOCKOWNER);
1421191783Srmacklem	if (local)
1422191783Srmacklem		newnfsstats.cllocallockowners--;
1423191783Srmacklem	else
1424191783Srmacklem		newnfsstats.cllockowners--;
1425191783Srmacklem}
1426191783Srmacklem
1427191783Srmacklem/*
1428191783Srmacklem * Free up a byte range lock structure.
1429191783Srmacklem */
1430191783SrmacklemAPPLESTATIC void
1431191783Srmacklemnfscl_freelock(struct nfscllock *lop, int local)
1432191783Srmacklem{
1433191783Srmacklem
1434191783Srmacklem	LIST_REMOVE(lop, nfslo_list);
1435191783Srmacklem	FREE((caddr_t)lop, M_NFSCLLOCK);
1436191783Srmacklem	if (local)
1437191783Srmacklem		newnfsstats.cllocallocks--;
1438191783Srmacklem	else
1439191783Srmacklem		newnfsstats.cllocks--;
1440191783Srmacklem}
1441191783Srmacklem
1442191783Srmacklem/*
1443191783Srmacklem * Clean out the state related to a delegation.
1444191783Srmacklem */
1445191783Srmacklemstatic void
1446191783Srmacklemnfscl_cleandeleg(struct nfscldeleg *dp)
1447191783Srmacklem{
1448191783Srmacklem	struct nfsclowner *owp, *nowp;
1449191783Srmacklem	struct nfsclopen *op;
1450191783Srmacklem
1451191783Srmacklem	LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
1452191783Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
1453191783Srmacklem		if (op != NULL) {
1454191783Srmacklem			if (LIST_NEXT(op, nfso_list) != NULL)
1455191783Srmacklem				panic("nfscleandel");
1456191783Srmacklem			nfscl_freeopen(op, 1);
1457191783Srmacklem		}
1458191783Srmacklem		nfscl_freeopenowner(owp, 1);
1459191783Srmacklem	}
1460191783Srmacklem	nfscl_freealllocks(&dp->nfsdl_lock, 1);
1461191783Srmacklem}
1462191783Srmacklem
1463191783Srmacklem/*
1464191783Srmacklem * Free a delegation.
1465191783Srmacklem */
1466191783Srmacklemstatic void
1467191783Srmacklemnfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp)
1468191783Srmacklem{
1469191783Srmacklem
1470191783Srmacklem	TAILQ_REMOVE(hdp, dp, nfsdl_list);
1471191783Srmacklem	LIST_REMOVE(dp, nfsdl_hash);
1472191783Srmacklem	FREE((caddr_t)dp, M_NFSCLDELEG);
1473191783Srmacklem	newnfsstats.cldelegates--;
1474191783Srmacklem	nfscl_delegcnt--;
1475191783Srmacklem}
1476191783Srmacklem
1477191783Srmacklem/*
1478191783Srmacklem * Free up all state related to this client structure.
1479191783Srmacklem */
1480191783Srmacklemstatic void
1481191783Srmacklemnfscl_cleanclient(struct nfsclclient *clp)
1482191783Srmacklem{
1483191783Srmacklem	struct nfsclowner *owp, *nowp;
1484191783Srmacklem	struct nfsclopen *op, *nop;
1485191783Srmacklem	struct nfscllockowner *lp, *nlp;
1486191783Srmacklem
1487191783Srmacklem
1488191783Srmacklem	/* get rid of defunct lockowners */
1489191783Srmacklem	LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, nlp) {
1490191783Srmacklem		nfscl_freelockowner(lp, 0);
1491191783Srmacklem	}
1492191783Srmacklem
1493191783Srmacklem	/* Now, all the OpenOwners, etc. */
1494191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1495191783Srmacklem		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1496191783Srmacklem			nfscl_freeopen(op, 0);
1497191783Srmacklem		}
1498191783Srmacklem		nfscl_freeopenowner(owp, 0);
1499191783Srmacklem	}
1500191783Srmacklem}
1501191783Srmacklem
1502191783Srmacklem/*
1503191783Srmacklem * Called when an NFSERR_EXPIRED is received from the server.
1504191783Srmacklem */
1505191783Srmacklemstatic void
1506191783Srmacklemnfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
1507191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
1508191783Srmacklem{
1509191783Srmacklem	struct nfsclowner *owp, *nowp, *towp;
1510191783Srmacklem	struct nfsclopen *op, *nop, *top;
1511191783Srmacklem	struct nfscldeleg *dp, *ndp;
1512191783Srmacklem	int ret, printed = 0;
1513191783Srmacklem
1514191783Srmacklem	/*
1515191783Srmacklem	 * First, merge locally issued Opens into the list for the server.
1516191783Srmacklem	 */
1517191783Srmacklem	dp = TAILQ_FIRST(&clp->nfsc_deleg);
1518191783Srmacklem	while (dp != NULL) {
1519191783Srmacklem	    ndp = TAILQ_NEXT(dp, nfsdl_list);
1520191783Srmacklem	    owp = LIST_FIRST(&dp->nfsdl_owner);
1521191783Srmacklem	    while (owp != NULL) {
1522191783Srmacklem		nowp = LIST_NEXT(owp, nfsow_list);
1523191783Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
1524191783Srmacklem		if (op != NULL) {
1525191783Srmacklem		    if (LIST_NEXT(op, nfso_list) != NULL)
1526191783Srmacklem			panic("nfsclexp");
1527191783Srmacklem		    LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) {
1528191783Srmacklem			if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner,
1529191783Srmacklem			    NFSV4CL_LOCKNAMELEN))
1530191783Srmacklem			    break;
1531191783Srmacklem		    }
1532191783Srmacklem		    if (towp != NULL) {
1533191783Srmacklem			/* Merge opens in */
1534191783Srmacklem			LIST_FOREACH(top, &towp->nfsow_open, nfso_list) {
1535191783Srmacklem			    if (top->nfso_fhlen == op->nfso_fhlen &&
1536191783Srmacklem				!NFSBCMP(top->nfso_fh, op->nfso_fh,
1537191783Srmacklem				 op->nfso_fhlen)) {
1538191783Srmacklem				top->nfso_mode |= op->nfso_mode;
1539191783Srmacklem				top->nfso_opencnt += op->nfso_opencnt;
1540191783Srmacklem				break;
1541191783Srmacklem			    }
1542191783Srmacklem			}
1543191783Srmacklem			if (top == NULL) {
1544191783Srmacklem			    /* Just add the open to the owner list */
1545191783Srmacklem			    LIST_REMOVE(op, nfso_list);
1546191783Srmacklem			    op->nfso_own = towp;
1547191783Srmacklem			    LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list);
1548191783Srmacklem			    newnfsstats.cllocalopens--;
1549191783Srmacklem			    newnfsstats.clopens++;
1550191783Srmacklem			}
1551191783Srmacklem		    } else {
1552191783Srmacklem			/* Just add the openowner to the client list */
1553191783Srmacklem			LIST_REMOVE(owp, nfsow_list);
1554191783Srmacklem			owp->nfsow_clp = clp;
1555191783Srmacklem			LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list);
1556191783Srmacklem			newnfsstats.cllocalopenowners--;
1557191783Srmacklem			newnfsstats.clopenowners++;
1558191783Srmacklem			newnfsstats.cllocalopens--;
1559191783Srmacklem			newnfsstats.clopens++;
1560191783Srmacklem		    }
1561191783Srmacklem		}
1562191783Srmacklem		owp = nowp;
1563191783Srmacklem	    }
1564191783Srmacklem	    if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) {
1565191783Srmacklem		printed = 1;
1566191783Srmacklem		printf("nfsv4 expired locks lost\n");
1567191783Srmacklem	    }
1568191783Srmacklem	    nfscl_cleandeleg(dp);
1569191783Srmacklem	    nfscl_freedeleg(&clp->nfsc_deleg, dp);
1570191783Srmacklem	    dp = ndp;
1571191783Srmacklem	}
1572191783Srmacklem	if (!TAILQ_EMPTY(&clp->nfsc_deleg))
1573191783Srmacklem	    panic("nfsclexp");
1574191783Srmacklem
1575191783Srmacklem	/*
1576191783Srmacklem	 * Now, try and reopen against the server.
1577191783Srmacklem	 */
1578191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1579191783Srmacklem		owp->nfsow_seqid = 0;
1580191783Srmacklem		LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1581191783Srmacklem			ret = nfscl_expireopen(clp, op, nmp, cred, p);
1582191783Srmacklem			if (ret && !printed) {
1583191783Srmacklem				printed = 1;
1584191783Srmacklem				printf("nfsv4 expired locks lost\n");
1585191783Srmacklem			}
1586191783Srmacklem		}
1587191783Srmacklem		if (LIST_EMPTY(&owp->nfsow_open))
1588191783Srmacklem			nfscl_freeopenowner(owp, 0);
1589191783Srmacklem	}
1590191783Srmacklem}
1591191783Srmacklem
1592191783Srmacklem#ifndef	__FreeBSD__
1593191783Srmacklem/*
1594191783Srmacklem * Called from exit() upon process termination.
1595191783Srmacklem */
1596191783SrmacklemAPPLESTATIC void
1597191783Srmacklemnfscl_cleanup(NFSPROC_T *p)
1598191783Srmacklem{
1599191783Srmacklem	struct nfsclclient *clp;
1600191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
1601191783Srmacklem
1602191783Srmacklem	if (!nfscl_inited)
1603191783Srmacklem		return;
1604191783Srmacklem	nfscl_filllockowner(p, own);
1605191783Srmacklem
1606191783Srmacklem	NFSLOCKCLSTATE();
1607191783Srmacklem	/*
1608191783Srmacklem	 * Loop through all the clientids, looking for the OpenOwners.
1609191783Srmacklem	 */
1610191783Srmacklem	LIST_FOREACH(clp, &nfsclhead, nfsc_list)
1611191783Srmacklem		nfscl_cleanup_common(clp, own);
1612191783Srmacklem	NFSUNLOCKCLSTATE();
1613191783Srmacklem}
1614191783Srmacklem#endif	/* !__FreeBSD__ */
1615191783Srmacklem
1616191783Srmacklem/*
1617191783Srmacklem * Common code used by nfscl_cleanup() and nfscl_cleanupkext().
1618191783Srmacklem * Must be called with CLSTATE lock held.
1619191783Srmacklem */
1620191783Srmacklemstatic void
1621191783Srmacklemnfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own)
1622191783Srmacklem{
1623191783Srmacklem	struct nfsclowner *owp, *nowp;
1624191783Srmacklem	struct nfsclopen *op;
1625191783Srmacklem	struct nfscllockowner *lp, *nlp;
1626191783Srmacklem	struct nfscldeleg *dp;
1627191783Srmacklem
1628191783Srmacklem	/* First, get rid of local locks on delegations. */
1629191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1630191783Srmacklem		LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) {
1631191783Srmacklem		    if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
1632191783Srmacklem			if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1633191783Srmacklem			    panic("nfscllckw");
1634191783Srmacklem			nfscl_freelockowner(lp, 1);
1635191783Srmacklem		    }
1636191783Srmacklem		}
1637191783Srmacklem	}
1638191783Srmacklem	owp = LIST_FIRST(&clp->nfsc_owner);
1639191783Srmacklem	while (owp != NULL) {
1640191783Srmacklem		nowp = LIST_NEXT(owp, nfsow_list);
1641191783Srmacklem		if (!NFSBCMP(owp->nfsow_owner, own,
1642191783Srmacklem		    NFSV4CL_LOCKNAMELEN)) {
1643191783Srmacklem			/*
1644191783Srmacklem			 * If there are children that haven't closed the
1645191783Srmacklem			 * file descriptors yet, the opens will still be
1646191783Srmacklem			 * here. For that case, let the renew thread clear
1647191783Srmacklem			 * out the OpenOwner later.
1648191783Srmacklem			 */
1649191783Srmacklem			if (LIST_EMPTY(&owp->nfsow_open))
1650191783Srmacklem				nfscl_freeopenowner(owp, 0);
1651191783Srmacklem			else
1652191783Srmacklem				owp->nfsow_defunct = 1;
1653191783Srmacklem		} else {
1654191783Srmacklem			/* look for lockowners on other opens */
1655191783Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1656191783Srmacklem				LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1657191783Srmacklem					if (!NFSBCMP(lp->nfsl_owner, own,
1658191783Srmacklem					    NFSV4CL_LOCKNAMELEN))
1659191783Srmacklem						lp->nfsl_defunct = 1;
1660191783Srmacklem				}
1661191783Srmacklem			}
1662191783Srmacklem		}
1663191783Srmacklem		owp = nowp;
1664191783Srmacklem	}
1665191783Srmacklem
1666191783Srmacklem	/* and check the defunct list */
1667191783Srmacklem	LIST_FOREACH(lp, &clp->nfsc_defunctlockowner, nfsl_list) {
1668191783Srmacklem		if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN))
1669191783Srmacklem		    lp->nfsl_defunct = 1;
1670191783Srmacklem	}
1671191783Srmacklem}
1672191783Srmacklem
1673191783Srmacklem#if defined(APPLEKEXT) || defined(__FreeBSD__)
1674191783Srmacklem/*
1675191783Srmacklem * Simulate the call nfscl_cleanup() by looking for open owners associated
1676191783Srmacklem * with processes that no longer exist, since a call to nfscl_cleanup()
1677191783Srmacklem * can't be patched into exit().
1678191783Srmacklem */
1679191783Srmacklemstatic void
1680191783Srmacklemnfscl_cleanupkext(struct nfsclclient *clp)
1681191783Srmacklem{
1682191783Srmacklem	struct nfsclowner *owp, *nowp;
1683191783Srmacklem	struct nfscllockowner *lp;
1684191783Srmacklem
1685191783Srmacklem	NFSPROCLISTLOCK();
1686191783Srmacklem	NFSLOCKCLSTATE();
1687191783Srmacklem	LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1688191783Srmacklem		if (nfscl_procdoesntexist(owp->nfsow_owner))
1689191783Srmacklem			nfscl_cleanup_common(clp, owp->nfsow_owner);
1690191783Srmacklem	}
1691191783Srmacklem
1692191783Srmacklem	/* and check the defunct list */
1693191783Srmacklem	LIST_FOREACH(lp, &clp->nfsc_defunctlockowner, nfsl_list) {
1694191783Srmacklem		if (nfscl_procdoesntexist(lp->nfsl_owner))
1695191783Srmacklem			lp->nfsl_defunct = 1;
1696191783Srmacklem	}
1697191783Srmacklem	NFSUNLOCKCLSTATE();
1698191783Srmacklem	NFSPROCLISTUNLOCK();
1699191783Srmacklem}
1700191783Srmacklem#endif	/* APPLEKEXT || __FreeBSD__ */
1701191783Srmacklem
1702191783Srmacklem/*
1703191783Srmacklem * Called from nfs umount to free up the clientid.
1704191783Srmacklem */
1705191783SrmacklemAPPLESTATIC void
1706191783Srmacklemnfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
1707191783Srmacklem{
1708191783Srmacklem	struct nfsclclient *clp;
1709191783Srmacklem	struct ucred *cred;
1710191783Srmacklem	int igotlock;
1711191783Srmacklem
1712191783Srmacklem	clp = nmp->nm_clp;
1713191783Srmacklem	if (clp != NULL) {
1714191783Srmacklem		if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
1715191783Srmacklem			panic("nfscl umount");
1716191783Srmacklem
1717191783Srmacklem		/*
1718191783Srmacklem		 * First, handshake with the nfscl renew thread, to terminate
1719191783Srmacklem		 * it.
1720191783Srmacklem		 */
1721191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
1722191783Srmacklem		while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
1723191783Srmacklem			(void) tsleep((caddr_t)clp, PWAIT, "nfsclumnt", hz);
1724191783Srmacklem
1725191783Srmacklem		NFSLOCKCLSTATE();
1726191783Srmacklem		do {
1727191783Srmacklem			igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1728191783Srmacklem			    NFSCLSTATEMUTEXPTR);
1729191783Srmacklem		} while (!igotlock);
1730191783Srmacklem		NFSUNLOCKCLSTATE();
1731191783Srmacklem
1732191783Srmacklem		/*
1733191783Srmacklem		 * Free up all the state. It will expire on the server, but
1734191783Srmacklem		 * maybe we should do a SetClientId/SetClientIdConfirm so
1735191783Srmacklem		 * the server throws it away?
1736191783Srmacklem		 */
1737191783Srmacklem		LIST_REMOVE(clp, nfsc_list);
1738191783Srmacklem		nfscl_delegreturnall(clp, p);
1739191783Srmacklem		cred = newnfs_getcred();
1740191783Srmacklem		(void) nfsrpc_setclient(nmp, clp, cred, p);
1741191783Srmacklem		nfscl_cleanclient(clp);
1742191783Srmacklem		nmp->nm_clp = NULL;
1743191783Srmacklem		NFSFREECRED(cred);
1744191783Srmacklem		FREE((caddr_t)clp, M_NFSCLCLIENT);
1745191783Srmacklem	}
1746191783Srmacklem
1747191783Srmacklem}
1748191783Srmacklem
1749191783Srmacklem/*
1750191783Srmacklem * This function is called when a server replies with NFSERR_STALECLIENTID
1751191783Srmacklem * or NFSERR_STALESTATEID. It traverses the clientid lists, doing Opens
1752191783Srmacklem * and Locks with reclaim. If these fail, it deletes the corresponding state.
1753191783Srmacklem */
1754191783Srmacklemstatic void
1755191783Srmacklemnfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
1756191783Srmacklem{
1757191783Srmacklem	struct nfsclowner *owp, *nowp;
1758191783Srmacklem	struct nfsclopen *op, *nop;
1759191783Srmacklem	struct nfscllockowner *lp, *nlp;
1760191783Srmacklem	struct nfscllock *lop, *nlop;
1761191783Srmacklem	struct nfscldeleg *dp, *ndp, *tdp;
1762191783Srmacklem	struct nfsmount *nmp;
1763191783Srmacklem	struct ucred *tcred;
1764191783Srmacklem	struct nfsclopenhead extra_open;
1765191783Srmacklem	struct nfscldeleghead extra_deleg;
1766191783Srmacklem	struct nfsreq *rep;
1767191783Srmacklem	u_int64_t len;
1768191783Srmacklem	u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
1769191783Srmacklem	int igotlock = 0, error, trycnt, firstlock, s;
1770191783Srmacklem
1771191783Srmacklem	/*
1772191783Srmacklem	 * First, lock the client structure, so everyone else will
1773191783Srmacklem	 * block when trying to use state.
1774191783Srmacklem	 */
1775191783Srmacklem	NFSLOCKCLSTATE();
1776191783Srmacklem	do {
1777191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1778191783Srmacklem		    NFSCLSTATEMUTEXPTR);
1779191783Srmacklem	} while (!igotlock);
1780191783Srmacklem	NFSUNLOCKCLSTATE();
1781191783Srmacklem
1782191783Srmacklem	nmp = clp->nfsc_nmp;
1783191783Srmacklem	if (nmp == NULL)
1784191783Srmacklem		panic("nfscl recover");
1785191783Srmacklem	trycnt = 5;
1786191783Srmacklem	do {
1787191783Srmacklem		error = nfsrpc_setclient(nmp, clp, cred, p);
1788191783Srmacklem	} while ((error == NFSERR_STALECLIENTID ||
1789191783Srmacklem	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
1790191783Srmacklem	if (error) {
1791191783Srmacklem		nfscl_cleanclient(clp);
1792191783Srmacklem		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
1793191783Srmacklem		    NFSCLFLAGS_RECOVER);
1794191783Srmacklem		NFSLOCKCLSTATE();
1795191783Srmacklem		nfsv4_unlock(&clp->nfsc_lock, 0);
1796191783Srmacklem		NFSUNLOCKCLSTATE();
1797191783Srmacklem		return;
1798191783Srmacklem	}
1799191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
1800191783Srmacklem	clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
1801191783Srmacklem
1802191783Srmacklem	/*
1803191783Srmacklem	 * Mark requests already queued on the server, so that they don't
1804191783Srmacklem	 * initiate another recovery cycle. Any requests already in the
1805191783Srmacklem	 * queue that handle state information will have the old stale
1806191783Srmacklem	 * clientid/stateid and will get a NFSERR_STALESTATEID or
1807191783Srmacklem	 * NFSERR_STALECLIENTID reply from the server. This will be
1808191783Srmacklem	 * translated to NFSERR_STALEDONTRECOVER when R_DONTRECOVER is set.
1809191783Srmacklem	 */
1810191783Srmacklem	s = splsoftclock();
1811191783Srmacklem	NFSLOCKREQ();
1812191783Srmacklem	TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) {
1813191783Srmacklem		if (rep->r_nmp == nmp)
1814191783Srmacklem			rep->r_flags |= R_DONTRECOVER;
1815191783Srmacklem	}
1816191783Srmacklem	NFSUNLOCKREQ();
1817191783Srmacklem	splx(s);
1818191783Srmacklem
1819191783Srmacklem	/* get rid of defunct lockowners */
1820191783Srmacklem	LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, nlp) {
1821191783Srmacklem		nfscl_freelockowner(lp, 0);
1822191783Srmacklem	}
1823191783Srmacklem
1824191783Srmacklem	/*
1825191783Srmacklem	 * Now, mark all delegations "need reclaim".
1826191783Srmacklem	 */
1827191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
1828191783Srmacklem		dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM;
1829191783Srmacklem
1830191783Srmacklem	TAILQ_INIT(&extra_deleg);
1831191783Srmacklem	LIST_INIT(&extra_open);
1832191783Srmacklem	/*
1833191783Srmacklem	 * Now traverse the state lists, doing Open and Lock Reclaims.
1834191783Srmacklem	 */
1835191783Srmacklem	tcred = newnfs_getcred();
1836191783Srmacklem	owp = LIST_FIRST(&clp->nfsc_owner);
1837191783Srmacklem	while (owp != NULL) {
1838191783Srmacklem	    nowp = LIST_NEXT(owp, nfsow_list);
1839191783Srmacklem	    owp->nfsow_seqid = 0;
1840191783Srmacklem	    op = LIST_FIRST(&owp->nfsow_open);
1841191783Srmacklem	    while (op != NULL) {
1842191783Srmacklem		nop = LIST_NEXT(op, nfso_list);
1843191783Srmacklem		if (error != NFSERR_NOGRACE) {
1844191783Srmacklem		    /* Search for a delegation to reclaim with the open */
1845191783Srmacklem		    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1846191783Srmacklem			if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1847191783Srmacklem			    continue;
1848191783Srmacklem			if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
1849191783Srmacklem			    mode = NFSV4OPEN_ACCESSWRITE;
1850191783Srmacklem			    delegtype = NFSV4OPEN_DELEGATEWRITE;
1851191783Srmacklem			} else {
1852191783Srmacklem			    mode = NFSV4OPEN_ACCESSREAD;
1853191783Srmacklem			    delegtype = NFSV4OPEN_DELEGATEREAD;
1854191783Srmacklem			}
1855191783Srmacklem			if ((op->nfso_mode & mode) == mode &&
1856191783Srmacklem			    op->nfso_fhlen == dp->nfsdl_fhlen &&
1857191783Srmacklem			    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen))
1858191783Srmacklem			    break;
1859191783Srmacklem		    }
1860191783Srmacklem		    ndp = dp;
1861191783Srmacklem		    if (dp == NULL)
1862191783Srmacklem			delegtype = NFSV4OPEN_DELEGATENONE;
1863191783Srmacklem		    newnfs_copycred(&op->nfso_cred, tcred);
1864191783Srmacklem		    error = nfscl_tryopen(nmp, NULL, op->nfso_fh,
1865191783Srmacklem			op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen,
1866191783Srmacklem			op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
1867191783Srmacklem			tcred, p);
1868191783Srmacklem		    if (!error) {
1869191783Srmacklem			/* Handle any replied delegation */
1870191783Srmacklem			if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
1871191783Srmacklem			    || NFSMNT_RDONLY(nmp->nm_mountp))) {
1872191783Srmacklem			    if ((ndp->nfsdl_flags & NFSCLDL_WRITE))
1873191783Srmacklem				mode = NFSV4OPEN_ACCESSWRITE;
1874191783Srmacklem			    else
1875191783Srmacklem				mode = NFSV4OPEN_ACCESSREAD;
1876191783Srmacklem			    TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1877191783Srmacklem				if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1878191783Srmacklem				    continue;
1879191783Srmacklem				if ((op->nfso_mode & mode) == mode &&
1880191783Srmacklem				    op->nfso_fhlen == dp->nfsdl_fhlen &&
1881191783Srmacklem				    !NFSBCMP(op->nfso_fh, dp->nfsdl_fh,
1882191783Srmacklem				    op->nfso_fhlen)) {
1883191783Srmacklem				    dp->nfsdl_stateid = ndp->nfsdl_stateid;
1884191783Srmacklem				    dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit;
1885191783Srmacklem				    dp->nfsdl_ace = ndp->nfsdl_ace;
1886191783Srmacklem				    dp->nfsdl_change = ndp->nfsdl_change;
1887191783Srmacklem				    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
1888191783Srmacklem				    if ((ndp->nfsdl_flags & NFSCLDL_RECALL))
1889191783Srmacklem					dp->nfsdl_flags |= NFSCLDL_RECALL;
1890191783Srmacklem				    FREE((caddr_t)ndp, M_NFSCLDELEG);
1891191783Srmacklem				    ndp = NULL;
1892191783Srmacklem				    break;
1893191783Srmacklem				}
1894191783Srmacklem			    }
1895191783Srmacklem			}
1896191783Srmacklem			if (ndp != NULL)
1897191783Srmacklem			    TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list);
1898191783Srmacklem
1899191783Srmacklem			/* and reclaim all byte range locks */
1900191783Srmacklem			lp = LIST_FIRST(&op->nfso_lock);
1901191783Srmacklem			while (lp != NULL) {
1902191783Srmacklem			    nlp = LIST_NEXT(lp, nfsl_list);
1903191783Srmacklem			    lp->nfsl_seqid = 0;
1904191783Srmacklem			    firstlock = 1;
1905191783Srmacklem			    lop = LIST_FIRST(&lp->nfsl_lock);
1906191783Srmacklem			    while (lop != NULL) {
1907191783Srmacklem				nlop = LIST_NEXT(lop, nfslo_list);
1908191783Srmacklem				if (lop->nfslo_end == NFS64BITSSET)
1909191783Srmacklem				    len = NFS64BITSSET;
1910191783Srmacklem				else
1911191783Srmacklem				    len = lop->nfslo_end - lop->nfslo_first;
1912191783Srmacklem				if (error != NFSERR_NOGRACE)
1913191783Srmacklem				    error = nfscl_trylock(nmp, NULL,
1914191783Srmacklem					op->nfso_fh, op->nfso_fhlen, lp,
1915191783Srmacklem					firstlock, 1, lop->nfslo_first, len,
1916191783Srmacklem					lop->nfslo_type, tcred, p);
1917191783Srmacklem				if (error != 0)
1918191783Srmacklem				    nfscl_freelock(lop, 0);
1919191783Srmacklem				else
1920191783Srmacklem				    firstlock = 0;
1921191783Srmacklem				lop = nlop;
1922191783Srmacklem			    }
1923191783Srmacklem			    /* If no locks, but a lockowner, just delete it. */
1924191783Srmacklem			    if (LIST_EMPTY(&lp->nfsl_lock))
1925191783Srmacklem				nfscl_freelockowner(lp, 0);
1926191783Srmacklem			    lp = nlp;
1927191783Srmacklem			}
1928191783Srmacklem		    } else {
1929191783Srmacklem			nfscl_freeopen(op, 0);
1930191783Srmacklem		    }
1931191783Srmacklem		}
1932191783Srmacklem		op = nop;
1933191783Srmacklem	    }
1934191783Srmacklem	    owp = nowp;
1935191783Srmacklem	}
1936191783Srmacklem
1937191783Srmacklem	/*
1938191783Srmacklem	 * Now, try and get any delegations not yet reclaimed by cobbling
1939191783Srmacklem	 * to-gether an appropriate open.
1940191783Srmacklem	 */
1941191783Srmacklem	nowp = NULL;
1942191783Srmacklem	dp = TAILQ_FIRST(&clp->nfsc_deleg);
1943191783Srmacklem	while (dp != NULL) {
1944191783Srmacklem	    ndp = TAILQ_NEXT(dp, nfsdl_list);
1945191783Srmacklem	    if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) {
1946191783Srmacklem		if (nowp == NULL) {
1947191783Srmacklem		    MALLOC(nowp, struct nfsclowner *,
1948191783Srmacklem			sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK);
1949191783Srmacklem		    /*
1950191783Srmacklem		     * Name must be as long an largest possible
1951191783Srmacklem		     * NFSV4CL_LOCKNAMELEN. 12 for now.
1952191783Srmacklem		     */
1953191783Srmacklem		    NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner,
1954191783Srmacklem			NFSV4CL_LOCKNAMELEN);
1955191783Srmacklem		    LIST_INIT(&nowp->nfsow_open);
1956191783Srmacklem		    nowp->nfsow_clp = clp;
1957191783Srmacklem		    nowp->nfsow_seqid = 0;
1958191783Srmacklem		    nowp->nfsow_defunct = 0;
1959191783Srmacklem		    nfscl_lockinit(&nowp->nfsow_rwlock);
1960191783Srmacklem		}
1961191783Srmacklem		nop = NULL;
1962191783Srmacklem		if (error != NFSERR_NOGRACE) {
1963191783Srmacklem		    MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
1964191783Srmacklem			dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
1965191783Srmacklem		    nop->nfso_own = nowp;
1966191783Srmacklem		    if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
1967191783Srmacklem			nop->nfso_mode = NFSV4OPEN_ACCESSWRITE;
1968191783Srmacklem			delegtype = NFSV4OPEN_DELEGATEWRITE;
1969191783Srmacklem		    } else {
1970191783Srmacklem			nop->nfso_mode = NFSV4OPEN_ACCESSREAD;
1971191783Srmacklem			delegtype = NFSV4OPEN_DELEGATEREAD;
1972191783Srmacklem		    }
1973191783Srmacklem		    nop->nfso_opencnt = 0;
1974191783Srmacklem		    nop->nfso_posixlock = 1;
1975191783Srmacklem		    nop->nfso_fhlen = dp->nfsdl_fhlen;
1976191783Srmacklem		    NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen);
1977191783Srmacklem		    LIST_INIT(&nop->nfso_lock);
1978191783Srmacklem		    nop->nfso_stateid.seqid = 0;
1979191783Srmacklem		    nop->nfso_stateid.other[0] = 0;
1980191783Srmacklem		    nop->nfso_stateid.other[1] = 0;
1981191783Srmacklem		    nop->nfso_stateid.other[2] = 0;
1982191783Srmacklem		    newnfs_copycred(&dp->nfsdl_cred, tcred);
1983191783Srmacklem		    newnfs_copyincred(tcred, &nop->nfso_cred);
1984191783Srmacklem		    tdp = NULL;
1985191783Srmacklem		    error = nfscl_tryopen(nmp, NULL, nop->nfso_fh,
1986191783Srmacklem			nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen,
1987191783Srmacklem			nop->nfso_mode, nop, NULL, 0, &tdp, 1,
1988191783Srmacklem			delegtype, tcred, p);
1989191783Srmacklem		    if (tdp != NULL) {
1990191783Srmacklem			if ((tdp->nfsdl_flags & NFSCLDL_WRITE))
1991191783Srmacklem			    mode = NFSV4OPEN_ACCESSWRITE;
1992191783Srmacklem			else
1993191783Srmacklem			    mode = NFSV4OPEN_ACCESSREAD;
1994191783Srmacklem			if ((nop->nfso_mode & mode) == mode &&
1995191783Srmacklem			    nop->nfso_fhlen == tdp->nfsdl_fhlen &&
1996191783Srmacklem			    !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh,
1997191783Srmacklem			    nop->nfso_fhlen)) {
1998191783Srmacklem			    dp->nfsdl_stateid = tdp->nfsdl_stateid;
1999191783Srmacklem			    dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit;
2000191783Srmacklem			    dp->nfsdl_ace = tdp->nfsdl_ace;
2001191783Srmacklem			    dp->nfsdl_change = tdp->nfsdl_change;
2002191783Srmacklem			    dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2003191783Srmacklem			    if ((tdp->nfsdl_flags & NFSCLDL_RECALL))
2004191783Srmacklem				dp->nfsdl_flags |= NFSCLDL_RECALL;
2005191783Srmacklem			    FREE((caddr_t)tdp, M_NFSCLDELEG);
2006191783Srmacklem			} else {
2007191783Srmacklem			    TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list);
2008191783Srmacklem			}
2009191783Srmacklem		    }
2010191783Srmacklem		}
2011191783Srmacklem		if (error) {
2012191783Srmacklem		    if (nop != NULL)
2013191783Srmacklem			FREE((caddr_t)nop, M_NFSCLOPEN);
2014191783Srmacklem		    /*
2015191783Srmacklem		     * Couldn't reclaim it, so throw the state
2016191783Srmacklem		     * away. Ouch!!
2017191783Srmacklem		     */
2018191783Srmacklem		    nfscl_cleandeleg(dp);
2019191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
2020191783Srmacklem		} else {
2021191783Srmacklem		    LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
2022191783Srmacklem		}
2023191783Srmacklem	    }
2024191783Srmacklem	    dp = ndp;
2025191783Srmacklem	}
2026191783Srmacklem
2027191783Srmacklem	/*
2028191783Srmacklem	 * Now, get rid of extra Opens and Delegations.
2029191783Srmacklem	 */
2030191783Srmacklem	LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) {
2031191783Srmacklem		do {
2032191783Srmacklem			newnfs_copycred(&op->nfso_cred, tcred);
2033191783Srmacklem			error = nfscl_tryclose(op, tcred, nmp, p);
2034191783Srmacklem			if (error == NFSERR_GRACE)
2035191783Srmacklem				(void) nfs_catnap(PZERO, "nfsexcls");
2036191783Srmacklem		} while (error == NFSERR_GRACE);
2037191783Srmacklem		LIST_REMOVE(op, nfso_list);
2038191783Srmacklem		FREE((caddr_t)op, M_NFSCLOPEN);
2039191783Srmacklem	}
2040191783Srmacklem	if (nowp != NULL)
2041191783Srmacklem		FREE((caddr_t)nowp, M_NFSCLOWNER);
2042191783Srmacklem
2043191783Srmacklem	TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) {
2044191783Srmacklem		do {
2045191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, tcred);
2046191783Srmacklem			error = nfscl_trydelegreturn(dp, tcred, nmp, p);
2047191783Srmacklem			if (error == NFSERR_GRACE)
2048191783Srmacklem				(void) nfs_catnap(PZERO, "nfsexdlg");
2049191783Srmacklem		} while (error == NFSERR_GRACE);
2050191783Srmacklem		TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list);
2051191783Srmacklem		FREE((caddr_t)dp, M_NFSCLDELEG);
2052191783Srmacklem	}
2053191783Srmacklem
2054191783Srmacklem	NFSLOCKCLSTATE();
2055191783Srmacklem	nfsv4_unlock(&clp->nfsc_lock, 0);
2056191783Srmacklem	NFSUNLOCKCLSTATE();
2057191783Srmacklem	NFSFREECRED(tcred);
2058191783Srmacklem}
2059191783Srmacklem
2060191783Srmacklem/*
2061191783Srmacklem * This function is called when a server replies with NFSERR_EXPIRED.
2062191783Srmacklem * It deletes all state for the client and does a fresh SetClientId/confirm.
2063191783Srmacklem * XXX Someday it should post a signal to the process(es) that hold the
2064191783Srmacklem * state, so they know that lock state has been lost.
2065191783Srmacklem */
2066191783SrmacklemAPPLESTATIC int
2067191783Srmacklemnfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
2068191783Srmacklem{
2069191783Srmacklem	struct nfscllockowner *lp, *nlp;
2070191783Srmacklem	struct nfsmount *nmp;
2071191783Srmacklem	struct ucred *cred;
2072191783Srmacklem	int igotlock = 0, error, trycnt;
2073191783Srmacklem
2074191783Srmacklem	/*
2075191783Srmacklem	 * If the clientid has gone away or a new SetClientid has already
2076191783Srmacklem	 * been done, just return ok.
2077191783Srmacklem	 */
2078191783Srmacklem	if (clp == NULL || clidrev != clp->nfsc_clientidrev)
2079191783Srmacklem		return (0);
2080191783Srmacklem
2081191783Srmacklem	/*
2082191783Srmacklem	 * First, lock the client structure, so everyone else will
2083191783Srmacklem	 * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so
2084191783Srmacklem	 * that only one thread does the work.
2085191783Srmacklem	 */
2086191783Srmacklem	NFSLOCKCLSTATE();
2087191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
2088191783Srmacklem	do {
2089191783Srmacklem		igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2090191783Srmacklem		    NFSCLSTATEMUTEXPTR);
2091191783Srmacklem	} while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
2092191783Srmacklem	if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
2093191783Srmacklem		if (igotlock)
2094191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
2095191783Srmacklem		NFSUNLOCKCLSTATE();
2096191783Srmacklem		return (0);
2097191783Srmacklem	}
2098191783Srmacklem	NFSUNLOCKCLSTATE();
2099191783Srmacklem
2100191783Srmacklem	nmp = clp->nfsc_nmp;
2101191783Srmacklem	if (nmp == NULL)
2102191783Srmacklem		panic("nfscl expired");
2103191783Srmacklem	cred = newnfs_getcred();
2104191783Srmacklem	trycnt = 5;
2105191783Srmacklem	do {
2106191783Srmacklem		error = nfsrpc_setclient(nmp, clp, cred, p);
2107191783Srmacklem	} while ((error == NFSERR_STALECLIENTID ||
2108191783Srmacklem	     error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
2109191783Srmacklem	if (error) {
2110191783Srmacklem		/*
2111191783Srmacklem		 * Clear out any state.
2112191783Srmacklem		 */
2113191783Srmacklem		nfscl_cleanclient(clp);
2114191783Srmacklem		clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
2115191783Srmacklem		    NFSCLFLAGS_RECOVER);
2116191783Srmacklem	} else {
2117191783Srmacklem		/* get rid of defunct lockowners */
2118191783Srmacklem		LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list,
2119191783Srmacklem		    nlp) {
2120191783Srmacklem			nfscl_freelockowner(lp, 0);
2121191783Srmacklem		}
2122191783Srmacklem
2123191783Srmacklem		/*
2124191783Srmacklem		 * Expire the state for the client.
2125191783Srmacklem		 */
2126191783Srmacklem		nfscl_expireclient(clp, nmp, cred, p);
2127191783Srmacklem		clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
2128191783Srmacklem		clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2129191783Srmacklem	}
2130191783Srmacklem	NFSFREECRED(cred);
2131191783Srmacklem	clp->nfsc_flags &= ~NFSCLFLAGS_EXPIREIT;
2132191783Srmacklem	NFSLOCKCLSTATE();
2133191783Srmacklem	nfsv4_unlock(&clp->nfsc_lock, 0);
2134191783Srmacklem	NFSUNLOCKCLSTATE();
2135191783Srmacklem	return (error);
2136191783Srmacklem}
2137191783Srmacklem
2138191783Srmacklem/*
2139191783Srmacklem * This function inserts a lock in the list after insert_lop.
2140191783Srmacklem */
2141191783Srmacklemstatic void
2142191783Srmacklemnfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop,
2143191783Srmacklem    struct nfscllock *insert_lop, int local)
2144191783Srmacklem{
2145191783Srmacklem
2146191783Srmacklem	if ((struct nfscllockowner *)insert_lop == lp)
2147191783Srmacklem		LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list);
2148191783Srmacklem	else
2149191783Srmacklem		LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list);
2150191783Srmacklem	if (local)
2151191783Srmacklem		newnfsstats.cllocallocks++;
2152191783Srmacklem	else
2153191783Srmacklem		newnfsstats.cllocks++;
2154191783Srmacklem}
2155191783Srmacklem
2156191783Srmacklem/*
2157191783Srmacklem * This function updates the locking for a lock owner and given file. It
2158191783Srmacklem * maintains a list of lock ranges ordered on increasing file offset that
2159191783Srmacklem * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style).
2160191783Srmacklem * It always adds new_lop to the list and sometimes uses the one pointed
2161191783Srmacklem * at by other_lopp.
2162191783Srmacklem * Returns 1 if the locks were modified, 0 otherwise.
2163191783Srmacklem */
2164191783Srmacklemstatic int
2165191783Srmacklemnfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp,
2166191783Srmacklem    struct nfscllock **other_lopp, int local)
2167191783Srmacklem{
2168191783Srmacklem	struct nfscllock *new_lop = *new_lopp;
2169191783Srmacklem	struct nfscllock *lop, *tlop, *ilop;
2170191783Srmacklem	struct nfscllock *other_lop;
2171191783Srmacklem	int unlock = 0, modified = 0;
2172191783Srmacklem	u_int64_t tmp;
2173191783Srmacklem
2174191783Srmacklem	/*
2175191783Srmacklem	 * Work down the list until the lock is merged.
2176191783Srmacklem	 */
2177191783Srmacklem	if (new_lop->nfslo_type == F_UNLCK)
2178191783Srmacklem		unlock = 1;
2179191783Srmacklem	ilop = (struct nfscllock *)lp;
2180191783Srmacklem	lop = LIST_FIRST(&lp->nfsl_lock);
2181191783Srmacklem	while (lop != NULL) {
2182191783Srmacklem	    /*
2183191783Srmacklem	     * Only check locks for this file that aren't before the start of
2184191783Srmacklem	     * new lock's range.
2185191783Srmacklem	     */
2186191783Srmacklem	    if (lop->nfslo_end >= new_lop->nfslo_first) {
2187191783Srmacklem		if (new_lop->nfslo_end < lop->nfslo_first) {
2188191783Srmacklem		    /*
2189191783Srmacklem		     * If the new lock ends before the start of the
2190191783Srmacklem		     * current lock's range, no merge, just insert
2191191783Srmacklem		     * the new lock.
2192191783Srmacklem		     */
2193191783Srmacklem		    break;
2194191783Srmacklem		}
2195191783Srmacklem		if (new_lop->nfslo_type == lop->nfslo_type ||
2196191783Srmacklem		    (new_lop->nfslo_first <= lop->nfslo_first &&
2197191783Srmacklem		     new_lop->nfslo_end >= lop->nfslo_end)) {
2198191783Srmacklem		    /*
2199191783Srmacklem		     * This lock can be absorbed by the new lock/unlock.
2200191783Srmacklem		     * This happens when it covers the entire range
2201191783Srmacklem		     * of the old lock or is contiguous
2202191783Srmacklem		     * with the old lock and is of the same type or an
2203191783Srmacklem		     * unlock.
2204191783Srmacklem		     */
2205191783Srmacklem		    if (new_lop->nfslo_type != lop->nfslo_type ||
2206191783Srmacklem			new_lop->nfslo_first != lop->nfslo_first ||
2207191783Srmacklem			new_lop->nfslo_end != lop->nfslo_end)
2208191783Srmacklem			modified = 1;
2209191783Srmacklem		    if (lop->nfslo_first < new_lop->nfslo_first)
2210191783Srmacklem			new_lop->nfslo_first = lop->nfslo_first;
2211191783Srmacklem		    if (lop->nfslo_end > new_lop->nfslo_end)
2212191783Srmacklem			new_lop->nfslo_end = lop->nfslo_end;
2213191783Srmacklem		    tlop = lop;
2214191783Srmacklem		    lop = LIST_NEXT(lop, nfslo_list);
2215191783Srmacklem		    nfscl_freelock(tlop, local);
2216191783Srmacklem		    continue;
2217191783Srmacklem		}
2218191783Srmacklem
2219191783Srmacklem		/*
2220191783Srmacklem		 * All these cases are for contiguous locks that are not the
2221191783Srmacklem		 * same type, so they can't be merged.
2222191783Srmacklem		 */
2223191783Srmacklem		if (new_lop->nfslo_first <= lop->nfslo_first) {
2224191783Srmacklem		    /*
2225191783Srmacklem		     * This case is where the new lock overlaps with the
2226191783Srmacklem		     * first part of the old lock. Move the start of the
2227191783Srmacklem		     * old lock to just past the end of the new lock. The
2228191783Srmacklem		     * new lock will be inserted in front of the old, since
2229191783Srmacklem		     * ilop hasn't been updated. (We are done now.)
2230191783Srmacklem		     */
2231191783Srmacklem		    if (lop->nfslo_first != new_lop->nfslo_end) {
2232191783Srmacklem			lop->nfslo_first = new_lop->nfslo_end;
2233191783Srmacklem			modified = 1;
2234191783Srmacklem		    }
2235191783Srmacklem		    break;
2236191783Srmacklem		}
2237191783Srmacklem		if (new_lop->nfslo_end >= lop->nfslo_end) {
2238191783Srmacklem		    /*
2239191783Srmacklem		     * This case is where the new lock overlaps with the
2240191783Srmacklem		     * end of the old lock's range. Move the old lock's
2241191783Srmacklem		     * end to just before the new lock's first and insert
2242191783Srmacklem		     * the new lock after the old lock.
2243191783Srmacklem		     * Might not be done yet, since the new lock could
2244191783Srmacklem		     * overlap further locks with higher ranges.
2245191783Srmacklem		     */
2246191783Srmacklem		    if (lop->nfslo_end != new_lop->nfslo_first) {
2247191783Srmacklem			lop->nfslo_end = new_lop->nfslo_first;
2248191783Srmacklem			modified = 1;
2249191783Srmacklem		    }
2250191783Srmacklem		    ilop = lop;
2251191783Srmacklem		    lop = LIST_NEXT(lop, nfslo_list);
2252191783Srmacklem		    continue;
2253191783Srmacklem		}
2254191783Srmacklem		/*
2255191783Srmacklem		 * The final case is where the new lock's range is in the
2256191783Srmacklem		 * middle of the current lock's and splits the current lock
2257191783Srmacklem		 * up. Use *other_lopp to handle the second part of the
2258191783Srmacklem		 * split old lock range. (We are done now.)
2259191783Srmacklem		 * For unlock, we use new_lop as other_lop and tmp, since
2260191783Srmacklem		 * other_lop and new_lop are the same for this case.
2261191783Srmacklem		 * We noted the unlock case above, so we don't need
2262191783Srmacklem		 * new_lop->nfslo_type any longer.
2263191783Srmacklem		 */
2264191783Srmacklem		tmp = new_lop->nfslo_first;
2265191783Srmacklem		if (unlock) {
2266191783Srmacklem		    other_lop = new_lop;
2267191783Srmacklem		    *new_lopp = NULL;
2268191783Srmacklem		} else {
2269191783Srmacklem		    other_lop = *other_lopp;
2270191783Srmacklem		    *other_lopp = NULL;
2271191783Srmacklem		}
2272191783Srmacklem		other_lop->nfslo_first = new_lop->nfslo_end;
2273191783Srmacklem		other_lop->nfslo_end = lop->nfslo_end;
2274191783Srmacklem		other_lop->nfslo_type = lop->nfslo_type;
2275191783Srmacklem		lop->nfslo_end = tmp;
2276191783Srmacklem		nfscl_insertlock(lp, other_lop, lop, local);
2277191783Srmacklem		ilop = lop;
2278191783Srmacklem		modified = 1;
2279191783Srmacklem		break;
2280191783Srmacklem	    }
2281191783Srmacklem	    ilop = lop;
2282191783Srmacklem	    lop = LIST_NEXT(lop, nfslo_list);
2283191783Srmacklem	    if (lop == NULL)
2284191783Srmacklem		break;
2285191783Srmacklem	}
2286191783Srmacklem
2287191783Srmacklem	/*
2288191783Srmacklem	 * Insert the new lock in the list at the appropriate place.
2289191783Srmacklem	 */
2290191783Srmacklem	if (!unlock) {
2291191783Srmacklem		nfscl_insertlock(lp, new_lop, ilop, local);
2292191783Srmacklem		*new_lopp = NULL;
2293191783Srmacklem		modified = 1;
2294191783Srmacklem	}
2295191783Srmacklem	return (modified);
2296191783Srmacklem}
2297191783Srmacklem
2298191783Srmacklem/*
2299191783Srmacklem * This function must be run as a kernel thread.
2300191783Srmacklem * It does Renew Ops and recovery, when required.
2301191783Srmacklem */
2302191783SrmacklemAPPLESTATIC void
2303191783Srmacklemnfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
2304191783Srmacklem{
2305191783Srmacklem	struct nfsclowner *owp, *nowp;
2306191783Srmacklem	struct nfsclopen *op;
2307191783Srmacklem	struct nfscllockowner *lp, *nlp, *olp;
2308191783Srmacklem	struct nfscldeleghead dh;
2309191783Srmacklem	struct nfscllockownerhead lh;
2310191783Srmacklem	struct nfscldeleg *dp, *ndp;
2311191783Srmacklem	struct ucred *cred;
2312191783Srmacklem	u_int32_t clidrev;
2313191783Srmacklem	int error, cbpathdown, islept, igotlock, ret, clearok;
2314191783Srmacklem
2315191783Srmacklem	cred = newnfs_getcred();
2316191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD;
2317191783Srmacklem	for(;;) {
2318191783Srmacklem		newnfs_setroot(cred);
2319191783Srmacklem		cbpathdown = 0;
2320191783Srmacklem		if (clp->nfsc_flags & NFSCLFLAGS_RECOVER)
2321191783Srmacklem			nfscl_recover(clp, cred, p);
2322191783Srmacklem		if (clp->nfsc_expire <= NFSD_MONOSEC &&
2323191783Srmacklem		    (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
2324191783Srmacklem			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
2325191783Srmacklem			clidrev = clp->nfsc_clientidrev;
2326191783Srmacklem			error = nfsrpc_renew(clp, cred, p);
2327191783Srmacklem			if (error == NFSERR_CBPATHDOWN)
2328191783Srmacklem			    cbpathdown = 1;
2329191783Srmacklem			else if (error == NFSERR_STALECLIENTID)
2330191783Srmacklem			    clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2331191783Srmacklem			else if (error == NFSERR_EXPIRED)
2332191783Srmacklem			    (void) nfscl_hasexpired(clp, clidrev, p);
2333191783Srmacklem		}
2334191783Srmacklem
2335191783Srmacklem		LIST_INIT(&lh);
2336191783Srmacklem		TAILQ_INIT(&dh);
2337191783Srmacklem		NFSLOCKCLSTATE();
2338191783Srmacklem		if (cbpathdown)
2339191783Srmacklem			/* It's a Total Recall! */
2340191783Srmacklem			nfscl_totalrecall(clp);
2341191783Srmacklem
2342191783Srmacklem		/*
2343191783Srmacklem		 * Now, handle defunct owners.
2344191783Srmacklem		 */
2345191783Srmacklem		owp = LIST_FIRST(&clp->nfsc_owner);
2346191783Srmacklem		while (owp != NULL) {
2347191783Srmacklem		    nowp = LIST_NEXT(owp, nfsow_list);
2348191783Srmacklem		    if (LIST_EMPTY(&owp->nfsow_open)) {
2349191783Srmacklem			if (owp->nfsow_defunct)
2350191783Srmacklem			    nfscl_freeopenowner(owp, 0);
2351191783Srmacklem		    } else {
2352191783Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2353191783Srmacklem			    lp = LIST_FIRST(&op->nfso_lock);
2354191783Srmacklem			    while (lp != NULL) {
2355191783Srmacklem				nlp = LIST_NEXT(lp, nfsl_list);
2356191783Srmacklem				if (lp->nfsl_defunct &&
2357191783Srmacklem				    LIST_EMPTY(&lp->nfsl_lock)) {
2358191783Srmacklem				    LIST_FOREACH(olp, &lh, nfsl_list) {
2359191783Srmacklem					if (!NFSBCMP(olp->nfsl_owner,
2360191783Srmacklem					    lp->nfsl_owner,NFSV4CL_LOCKNAMELEN))
2361191783Srmacklem					    break;
2362191783Srmacklem				    }
2363191783Srmacklem				    if (olp == NULL) {
2364191783Srmacklem					LIST_REMOVE(lp, nfsl_list);
2365191783Srmacklem					LIST_INSERT_HEAD(&lh, lp, nfsl_list);
2366191783Srmacklem				    } else {
2367191783Srmacklem					nfscl_freelockowner(lp, 0);
2368191783Srmacklem				    }
2369191783Srmacklem				}
2370191783Srmacklem				lp = nlp;
2371191783Srmacklem			    }
2372191783Srmacklem			}
2373191783Srmacklem		    }
2374191783Srmacklem		    owp = nowp;
2375191783Srmacklem		}
2376191783Srmacklem
2377191783Srmacklem		/* also search the defunct list */
2378191783Srmacklem		lp = LIST_FIRST(&clp->nfsc_defunctlockowner);
2379191783Srmacklem		while (lp != NULL) {
2380191783Srmacklem		    nlp = LIST_NEXT(lp, nfsl_list);
2381191783Srmacklem		    if (lp->nfsl_defunct) {
2382191783Srmacklem			LIST_FOREACH(olp, &lh, nfsl_list) {
2383191783Srmacklem			    if (!NFSBCMP(olp->nfsl_owner, lp->nfsl_owner,
2384191783Srmacklem				NFSV4CL_LOCKNAMELEN))
2385191783Srmacklem				break;
2386191783Srmacklem			}
2387191783Srmacklem			if (olp == NULL) {
2388191783Srmacklem			    LIST_REMOVE(lp, nfsl_list);
2389191783Srmacklem			    LIST_INSERT_HEAD(&lh, lp, nfsl_list);
2390191783Srmacklem			} else {
2391191783Srmacklem			    nfscl_freelockowner(lp, 0);
2392191783Srmacklem			}
2393191783Srmacklem		    }
2394191783Srmacklem		    lp = nlp;
2395191783Srmacklem		}
2396191783Srmacklem		/* and release defunct lock owners */
2397191783Srmacklem		LIST_FOREACH_SAFE(lp, &lh, nfsl_list, nlp) {
2398191783Srmacklem		    nfscl_freelockowner(lp, 0);
2399191783Srmacklem		}
2400191783Srmacklem
2401191783Srmacklem		/*
2402191783Srmacklem		 * Do the recall on any delegations. To avoid trouble, always
2403191783Srmacklem		 * come back up here after having slept.
2404191783Srmacklem		 */
2405191783Srmacklem		igotlock = 0;
2406191783Srmacklemtryagain:
2407191783Srmacklem		dp = TAILQ_FIRST(&clp->nfsc_deleg);
2408191783Srmacklem		while (dp != NULL) {
2409191783Srmacklem			ndp = TAILQ_NEXT(dp, nfsdl_list);
2410191783Srmacklem			if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
2411191783Srmacklem				/*
2412191783Srmacklem				 * Wait for outstanding I/O ops to be done.
2413191783Srmacklem				 */
2414191783Srmacklem				if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
2415191783Srmacklem				    if (igotlock) {
2416191783Srmacklem					nfsv4_unlock(&clp->nfsc_lock, 0);
2417191783Srmacklem					igotlock = 0;
2418191783Srmacklem				    }
2419191783Srmacklem				    dp->nfsdl_rwlock.nfslock_lock |=
2420191783Srmacklem					NFSV4LOCK_WANTED;
2421191783Srmacklem				    (void) nfsmsleep(&dp->nfsdl_rwlock,
2422191783Srmacklem					NFSCLSTATEMUTEXPTR, PZERO, "nfscld",
2423191783Srmacklem					NULL);
2424191783Srmacklem				    goto tryagain;
2425191783Srmacklem				}
2426191783Srmacklem				while (!igotlock) {
2427191783Srmacklem				    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
2428191783Srmacklem					&islept, NFSCLSTATEMUTEXPTR);
2429191783Srmacklem				    if (islept)
2430191783Srmacklem					goto tryagain;
2431191783Srmacklem				}
2432191783Srmacklem				NFSUNLOCKCLSTATE();
2433191783Srmacklem				newnfs_copycred(&dp->nfsdl_cred, cred);
2434191783Srmacklem				ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
2435191783Srmacklem				    NULL, cred, p);
2436191783Srmacklem				if (!ret) {
2437191783Srmacklem				    nfscl_cleandeleg(dp);
2438191783Srmacklem				    TAILQ_REMOVE(&clp->nfsc_deleg, dp,
2439191783Srmacklem					nfsdl_list);
2440191783Srmacklem				    LIST_REMOVE(dp, nfsdl_hash);
2441191783Srmacklem				    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2442191783Srmacklem				    nfscl_delegcnt--;
2443191783Srmacklem				    newnfsstats.cldelegates--;
2444191783Srmacklem				}
2445191783Srmacklem				NFSLOCKCLSTATE();
2446191783Srmacklem			}
2447191783Srmacklem			dp = ndp;
2448191783Srmacklem		}
2449191783Srmacklem
2450191783Srmacklem		/*
2451191783Srmacklem		 * Clear out old delegations, if we are above the high water
2452191783Srmacklem		 * mark. Only clear out ones with no state related to them.
2453191783Srmacklem		 * The tailq list is in LRU order.
2454191783Srmacklem		 */
2455191783Srmacklem		dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
2456191783Srmacklem		while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) {
2457191783Srmacklem		    ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
2458191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
2459191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock == 0 &&
2460191783Srmacklem			dp->nfsdl_timestamp < NFSD_MONOSEC &&
2461191783Srmacklem			!(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
2462191783Srmacklem			  NFSCLDL_NEEDRECLAIM))) {
2463191783Srmacklem			clearok = 1;
2464191783Srmacklem			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2465191783Srmacklem			    op = LIST_FIRST(&owp->nfsow_open);
2466195819Srmacklem			    if (op != NULL) {
2467191783Srmacklem				clearok = 0;
2468191783Srmacklem				break;
2469191783Srmacklem			    }
2470191783Srmacklem			}
2471191783Srmacklem			if (clearok) {
2472191783Srmacklem			    LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
2473191783Srmacklem				if (!LIST_EMPTY(&lp->nfsl_lock)) {
2474191783Srmacklem				    clearok = 0;
2475191783Srmacklem				    break;
2476191783Srmacklem				}
2477191783Srmacklem			    }
2478191783Srmacklem			}
2479191783Srmacklem			if (clearok) {
2480191783Srmacklem			    TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
2481191783Srmacklem			    LIST_REMOVE(dp, nfsdl_hash);
2482191783Srmacklem			    TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2483191783Srmacklem			    nfscl_delegcnt--;
2484191783Srmacklem			    newnfsstats.cldelegates--;
2485191783Srmacklem			}
2486191783Srmacklem		    }
2487191783Srmacklem		    dp = ndp;
2488191783Srmacklem		}
2489191783Srmacklem		if (igotlock)
2490191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
2491191783Srmacklem		NFSUNLOCKCLSTATE();
2492191783Srmacklem
2493191783Srmacklem		/*
2494191783Srmacklem		 * Delegreturn any delegations cleaned out or recalled.
2495191783Srmacklem		 */
2496191783Srmacklem		TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
2497191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
2498191783Srmacklem			(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2499191783Srmacklem			TAILQ_REMOVE(&dh, dp, nfsdl_list);
2500191783Srmacklem			FREE((caddr_t)dp, M_NFSCLDELEG);
2501191783Srmacklem		}
2502191783Srmacklem
2503191783Srmacklem#if defined(APPLEKEXT) || defined(__FreeBSD__)
2504191783Srmacklem		/*
2505191783Srmacklem		 * Simulate the calls to nfscl_cleanup() when a process
2506191783Srmacklem		 * exits, since the call can't be patched into exit().
2507191783Srmacklem		 */
2508191783Srmacklem		{
2509191783Srmacklem			struct timespec mytime;
2510191783Srmacklem			static time_t prevsec = 0;
2511191783Srmacklem
2512191783Srmacklem			NFSGETNANOTIME(&mytime);
2513191783Srmacklem			if (prevsec != mytime.tv_sec) {
2514191783Srmacklem				prevsec = mytime.tv_sec;
2515191783Srmacklem				nfscl_cleanupkext(clp);
2516191783Srmacklem			}
2517191783Srmacklem		}
2518191783Srmacklem#endif	/* APPLEKEXT || __FreeBSD__ */
2519191783Srmacklem
2520191783Srmacklem		if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
2521191783Srmacklem		    (void) tsleep((caddr_t)clp, PWAIT, "nfscl", hz);
2522191783Srmacklem		if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
2523191783Srmacklem			NFSFREECRED(cred);
2524191783Srmacklem			clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
2525191783Srmacklem			wakeup((caddr_t)clp);
2526191783Srmacklem			return;
2527191783Srmacklem		}
2528191783Srmacklem	}
2529191783Srmacklem}
2530191783Srmacklem
2531191783Srmacklem/*
2532191783Srmacklem * Initiate state recovery. Called when NFSERR_STALECLIENTID or
2533191783Srmacklem * NFSERR_STALESTATEID is received.
2534191783Srmacklem */
2535191783SrmacklemAPPLESTATIC void
2536191783Srmacklemnfscl_initiate_recovery(struct nfsclclient *clp)
2537191783Srmacklem{
2538191783Srmacklem
2539191783Srmacklem	if (clp == NULL)
2540191783Srmacklem		return;
2541191783Srmacklem	NFSLOCKCLSTATE();
2542191783Srmacklem	clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2543191783Srmacklem	NFSUNLOCKCLSTATE();
2544191783Srmacklem	wakeup((caddr_t)clp);
2545191783Srmacklem}
2546191783Srmacklem
2547191783Srmacklem/*
2548191783Srmacklem * Dump out the state stuff for debugging.
2549191783Srmacklem */
2550191783SrmacklemAPPLESTATIC void
2551191783Srmacklemnfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
2552191783Srmacklem    int lockowner, int locks)
2553191783Srmacklem{
2554191783Srmacklem	struct nfsclclient *clp;
2555191783Srmacklem	struct nfsclowner *owp;
2556191783Srmacklem	struct nfsclopen *op;
2557191783Srmacklem	struct nfscllockowner *lp;
2558191783Srmacklem	struct nfscllock *lop;
2559191783Srmacklem	struct nfscldeleg *dp;
2560191783Srmacklem
2561191783Srmacklem	clp = nmp->nm_clp;
2562191783Srmacklem	if (clp == NULL) {
2563191783Srmacklem		printf("nfscl dumpstate NULL clp\n");
2564191783Srmacklem		return;
2565191783Srmacklem	}
2566191783Srmacklem	NFSLOCKCLSTATE();
2567191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2568191783Srmacklem	  LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2569191783Srmacklem	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2570191783Srmacklem		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2571191783Srmacklem		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2572191783Srmacklem		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2573191783Srmacklem		    owp->nfsow_seqid);
2574191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2575191783Srmacklem		if (opens)
2576191783Srmacklem		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2577191783Srmacklem			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2578191783Srmacklem			op->nfso_stateid.other[2], op->nfso_opencnt,
2579191783Srmacklem			op->nfso_fh[12]);
2580191783Srmacklem		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2581191783Srmacklem		    if (lockowner)
2582191783Srmacklem			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2583191783Srmacklem			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2584191783Srmacklem			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2585191783Srmacklem			    lp->nfsl_seqid,
2586191783Srmacklem			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2587191783Srmacklem			    lp->nfsl_stateid.other[2]);
2588191783Srmacklem		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2589191783Srmacklem			if (locks)
2590191783Srmacklem#ifdef __FreeBSD__
2591191783Srmacklem			    printf("lck typ=%d fst=%ju end=%ju\n",
2592191783Srmacklem				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2593191783Srmacklem				(intmax_t)lop->nfslo_end);
2594191783Srmacklem#else
2595191783Srmacklem			    printf("lck typ=%d fst=%qd end=%qd\n",
2596191783Srmacklem				lop->nfslo_type, lop->nfslo_first,
2597191783Srmacklem				lop->nfslo_end);
2598191783Srmacklem#endif
2599191783Srmacklem		    }
2600191783Srmacklem		}
2601191783Srmacklem	    }
2602191783Srmacklem	  }
2603191783Srmacklem	}
2604191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2605191783Srmacklem	    if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2606191783Srmacklem		printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2607191783Srmacklem		    owp->nfsow_owner[0], owp->nfsow_owner[1],
2608191783Srmacklem		    owp->nfsow_owner[2], owp->nfsow_owner[3],
2609191783Srmacklem		    owp->nfsow_seqid);
2610191783Srmacklem	    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2611191783Srmacklem		if (opens)
2612191783Srmacklem		    printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2613191783Srmacklem			op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2614191783Srmacklem			op->nfso_stateid.other[2], op->nfso_opencnt,
2615191783Srmacklem			op->nfso_fh[12]);
2616191783Srmacklem		LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2617191783Srmacklem		    if (lockowner)
2618191783Srmacklem			printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2619191783Srmacklem			    lp->nfsl_owner[0], lp->nfsl_owner[1],
2620191783Srmacklem			    lp->nfsl_owner[2], lp->nfsl_owner[3],
2621191783Srmacklem			    lp->nfsl_seqid,
2622191783Srmacklem			    lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2623191783Srmacklem			    lp->nfsl_stateid.other[2]);
2624191783Srmacklem		    LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2625191783Srmacklem			if (locks)
2626191783Srmacklem#ifdef __FreeBSD__
2627191783Srmacklem			    printf("lck typ=%d fst=%ju end=%ju\n",
2628191783Srmacklem				lop->nfslo_type, (intmax_t)lop->nfslo_first,
2629191783Srmacklem				(intmax_t)lop->nfslo_end);
2630191783Srmacklem#else
2631191783Srmacklem			    printf("lck typ=%d fst=%qd end=%qd\n",
2632191783Srmacklem				lop->nfslo_type, lop->nfslo_first,
2633191783Srmacklem				lop->nfslo_end);
2634191783Srmacklem#endif
2635191783Srmacklem		    }
2636191783Srmacklem		}
2637191783Srmacklem	    }
2638191783Srmacklem	}
2639191783Srmacklem	NFSUNLOCKCLSTATE();
2640191783Srmacklem}
2641191783Srmacklem
2642191783Srmacklem/*
2643191783Srmacklem * Check for duplicate open owners and opens.
2644191783Srmacklem * (Only used as a diagnostic aid.)
2645191783Srmacklem */
2646191783SrmacklemAPPLESTATIC void
2647191783Srmacklemnfscl_dupopen(vnode_t vp, int dupopens)
2648191783Srmacklem{
2649191783Srmacklem	struct nfsclclient *clp;
2650191783Srmacklem	struct nfsclowner *owp, *owp2;
2651191783Srmacklem	struct nfsclopen *op, *op2;
2652191783Srmacklem	struct nfsfh *nfhp;
2653191783Srmacklem
2654191783Srmacklem	clp = VFSTONFS(vnode_mount(vp))->nm_clp;
2655191783Srmacklem	if (clp == NULL) {
2656191783Srmacklem		printf("nfscl dupopen NULL clp\n");
2657191783Srmacklem		return;
2658191783Srmacklem	}
2659191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
2660191783Srmacklem	NFSLOCKCLSTATE();
2661191783Srmacklem
2662191783Srmacklem	/*
2663191783Srmacklem	 * First, search for duplicate owners.
2664191783Srmacklem	 * These should never happen!
2665191783Srmacklem	 */
2666191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2667191783Srmacklem	    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2668191783Srmacklem		if (owp != owp2 &&
2669191783Srmacklem		    !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
2670191783Srmacklem		    NFSV4CL_LOCKNAMELEN)) {
2671191783Srmacklem			NFSUNLOCKCLSTATE();
2672191783Srmacklem			printf("DUP OWNER\n");
2673191783Srmacklem			nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0);
2674191783Srmacklem			return;
2675191783Srmacklem		}
2676191783Srmacklem	    }
2677191783Srmacklem	}
2678191783Srmacklem
2679191783Srmacklem	/*
2680191783Srmacklem	 * Now, search for duplicate stateids.
2681191783Srmacklem	 * These shouldn't happen, either.
2682191783Srmacklem	 */
2683191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2684191783Srmacklem	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2685191783Srmacklem		LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2686191783Srmacklem		    LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2687191783Srmacklem			if (op != op2 &&
2688191783Srmacklem			    (op->nfso_stateid.other[0] != 0 ||
2689191783Srmacklem			     op->nfso_stateid.other[1] != 0 ||
2690191783Srmacklem			     op->nfso_stateid.other[2] != 0) &&
2691191783Srmacklem			    op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
2692191783Srmacklem			    op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
2693191783Srmacklem			    op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
2694191783Srmacklem			    NFSUNLOCKCLSTATE();
2695191783Srmacklem			    printf("DUP STATEID\n");
2696191783Srmacklem			    nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0,
2697191783Srmacklem				0);
2698191783Srmacklem			    return;
2699191783Srmacklem			}
2700191783Srmacklem		    }
2701191783Srmacklem		}
2702191783Srmacklem	    }
2703191783Srmacklem	}
2704191783Srmacklem
2705191783Srmacklem	/*
2706191783Srmacklem	 * Now search for duplicate opens.
2707191783Srmacklem	 * Duplicate opens for the same owner
2708191783Srmacklem	 * should never occur. Other duplicates are
2709191783Srmacklem	 * possible and are checked for if "dupopens"
2710191783Srmacklem	 * is true.
2711191783Srmacklem	 */
2712191783Srmacklem	LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2713191783Srmacklem	    LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2714191783Srmacklem		if (nfhp->nfh_len == op2->nfso_fhlen &&
2715191783Srmacklem		    !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
2716191783Srmacklem		    LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2717191783Srmacklem			LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2718191783Srmacklem			    if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
2719191783Srmacklem				!NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
2720191783Srmacklem				(!NFSBCMP(op->nfso_own->nfsow_owner,
2721191783Srmacklem				 op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
2722191783Srmacklem				 dupopens)) {
2723191783Srmacklem				if (!NFSBCMP(op->nfso_own->nfsow_owner,
2724191783Srmacklem				    op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
2725191783Srmacklem				    NFSUNLOCKCLSTATE();
2726191783Srmacklem				    printf("BADDUP OPEN\n");
2727191783Srmacklem				} else {
2728191783Srmacklem				    NFSUNLOCKCLSTATE();
2729191783Srmacklem				    printf("DUP OPEN\n");
2730191783Srmacklem				}
2731191783Srmacklem				nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1,
2732191783Srmacklem				    0, 0);
2733191783Srmacklem				return;
2734191783Srmacklem			    }
2735191783Srmacklem			}
2736191783Srmacklem		    }
2737191783Srmacklem		}
2738191783Srmacklem	    }
2739191783Srmacklem	}
2740191783Srmacklem	NFSUNLOCKCLSTATE();
2741191783Srmacklem}
2742191783Srmacklem
2743191783Srmacklem/*
2744191783Srmacklem * During close, find an open that needs to be dereferenced and
2745191783Srmacklem * dereference it. If there are no more opens for this file,
2746195510Srmacklem * log a message to that effect.
2747195510Srmacklem * Opens aren't actually Close'd until VOP_INACTIVE() is performed
2748195510Srmacklem * on the file's vnode.
2749191783Srmacklem * This is the safe way, since it is difficult to identify
2750195510Srmacklem * which open the close is for and I/O can be performed after the
2751195510Srmacklem * close(2) system call when a file is mmap'd.
2752191783Srmacklem * If it returns 0 for success, there will be a referenced
2753195510Srmacklem * clp returned via clpp.
2754191783Srmacklem */
2755191783SrmacklemAPPLESTATIC int
2756195510Srmacklemnfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
2757191783Srmacklem{
2758191783Srmacklem	struct nfsclclient *clp;
2759195510Srmacklem	struct nfsclowner *owp;
2760195510Srmacklem	struct nfsclopen *op;
2761191783Srmacklem	struct nfscldeleg *dp;
2762191783Srmacklem	struct nfsfh *nfhp;
2763195510Srmacklem	int error, notdecr;
2764191783Srmacklem
2765192337Srmacklem	error = nfscl_getcl(vp, NULL, NULL, &clp);
2766191783Srmacklem	if (error)
2767191783Srmacklem		return (error);
2768191783Srmacklem	*clpp = clp;
2769191783Srmacklem
2770191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
2771191783Srmacklem	notdecr = 1;
2772191783Srmacklem	NFSLOCKCLSTATE();
2773191783Srmacklem	/*
2774191783Srmacklem	 * First, look for one under a delegation that was locally issued
2775191783Srmacklem	 * and just decrement the opencnt for it. Since all my Opens against
2776191783Srmacklem	 * the server are DENY_NONE, I don't see a problem with hanging
2777191783Srmacklem	 * onto them. (It is much easier to use one of the extant Opens
2778191783Srmacklem	 * that I already have on the server when a Delegation is recalled
2779195510Srmacklem	 * than to do fresh Opens.) Someday, I might need to rethink this, but.
2780191783Srmacklem	 */
2781191783Srmacklem	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
2782191783Srmacklem	if (dp != NULL) {
2783191783Srmacklem		LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2784191783Srmacklem			op = LIST_FIRST(&owp->nfsow_open);
2785191783Srmacklem			if (op != NULL) {
2786191783Srmacklem				/*
2787191783Srmacklem				 * Since a delegation is for a file, there
2788191783Srmacklem				 * should never be more than one open for
2789191783Srmacklem				 * each openowner.
2790191783Srmacklem				 */
2791191783Srmacklem				if (LIST_NEXT(op, nfso_list) != NULL)
2792191783Srmacklem					panic("nfscdeleg opens");
2793191783Srmacklem				if (notdecr && op->nfso_opencnt > 0) {
2794191783Srmacklem					notdecr = 0;
2795191783Srmacklem					op->nfso_opencnt--;
2796191783Srmacklem					break;
2797191783Srmacklem				}
2798191783Srmacklem			}
2799191783Srmacklem		}
2800191783Srmacklem	}
2801191783Srmacklem
2802191783Srmacklem	/* Now process the opens against the server. */
2803191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2804195510Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2805192337Srmacklem			if (op->nfso_fhlen == nfhp->nfh_len &&
2806192337Srmacklem			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
2807192337Srmacklem			    nfhp->nfh_len)) {
2808192337Srmacklem				/* Found an open, decrement cnt if possible */
2809192337Srmacklem				if (notdecr && op->nfso_opencnt > 0) {
2810192337Srmacklem					notdecr = 0;
2811192337Srmacklem					op->nfso_opencnt--;
2812192337Srmacklem				}
2813192337Srmacklem				/*
2814195510Srmacklem				 * There are more opens, so just return.
2815192337Srmacklem				 */
2816192337Srmacklem				if (op->nfso_opencnt > 0) {
2817192337Srmacklem					NFSUNLOCKCLSTATE();
2818192337Srmacklem					return (0);
2819192337Srmacklem				}
2820191783Srmacklem			}
2821191783Srmacklem		}
2822191783Srmacklem	}
2823195510Srmacklem	NFSUNLOCKCLSTATE();
2824195510Srmacklem	if (notdecr)
2825195510Srmacklem		printf("nfscl: never fnd open\n");
2826195510Srmacklem	return (0);
2827195510Srmacklem}
2828191783Srmacklem
2829195510SrmacklemAPPLESTATIC int
2830195510Srmacklemnfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
2831195510Srmacklem{
2832195510Srmacklem	struct nfsclclient *clp;
2833195510Srmacklem	struct nfsclowner *owp, *nowp;
2834195510Srmacklem	struct nfsclopen *op;
2835195510Srmacklem	struct nfscldeleg *dp;
2836195510Srmacklem	struct nfsfh *nfhp;
2837195510Srmacklem	int error;
2838195510Srmacklem
2839195510Srmacklem	error = nfscl_getcl(vp, NULL, NULL, &clp);
2840195510Srmacklem	if (error)
2841195510Srmacklem		return (error);
2842195510Srmacklem	*clpp = clp;
2843195510Srmacklem
2844195510Srmacklem	nfhp = VTONFS(vp)->n_fhp;
2845195510Srmacklem	NFSLOCKCLSTATE();
2846195510Srmacklem	/*
2847195510Srmacklem	 * First get rid of the local Open structures, which should be no
2848195510Srmacklem	 * longer in use.
2849195510Srmacklem	 */
2850195510Srmacklem	dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
2851195510Srmacklem	if (dp != NULL) {
2852195510Srmacklem		LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
2853191783Srmacklem			op = LIST_FIRST(&owp->nfsow_open);
2854195510Srmacklem			if (op != NULL) {
2855195510Srmacklem				KASSERT((op->nfso_opencnt == 0),
2856195510Srmacklem				    ("nfscl: bad open cnt on deleg"));
2857195510Srmacklem				nfscl_freeopen(op, 1);
2858191783Srmacklem			}
2859195510Srmacklem			nfscl_freeopenowner(owp, 1);
2860191783Srmacklem		}
2861195510Srmacklem	}
2862195510Srmacklem
2863195510Srmacklem	/* Now process the opens against the server. */
2864195510Srmacklemlookformore:
2865195510Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2866195510Srmacklem		op = LIST_FIRST(&owp->nfsow_open);
2867195510Srmacklem		while (op != NULL) {
2868195510Srmacklem			if (op->nfso_fhlen == nfhp->nfh_len &&
2869195510Srmacklem			    !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
2870195510Srmacklem			    nfhp->nfh_len)) {
2871195510Srmacklem				/* Found an open, close it. */
2872195510Srmacklem				KASSERT((op->nfso_opencnt == 0),
2873195510Srmacklem				    ("nfscl: bad open cnt on server"));
2874195510Srmacklem				NFSUNLOCKCLSTATE();
2875195510Srmacklem				nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op,
2876195510Srmacklem				    p);
2877195510Srmacklem				NFSLOCKCLSTATE();
2878195510Srmacklem				goto lookformore;
2879191783Srmacklem			}
2880195510Srmacklem			op = LIST_NEXT(op, nfso_list);
2881191783Srmacklem		}
2882191783Srmacklem	}
2883191783Srmacklem	NFSUNLOCKCLSTATE();
2884191783Srmacklem	return (0);
2885191783Srmacklem}
2886191783Srmacklem
2887191783Srmacklem/*
2888191783Srmacklem * Return all delegations on this client.
2889191783Srmacklem * (Must be called with client sleep lock.)
2890191783Srmacklem */
2891191783Srmacklemstatic void
2892191783Srmacklemnfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
2893191783Srmacklem{
2894191783Srmacklem	struct nfscldeleg *dp, *ndp;
2895191783Srmacklem	struct ucred *cred;
2896191783Srmacklem
2897191783Srmacklem	cred = newnfs_getcred();
2898191783Srmacklem	TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
2899191783Srmacklem		nfscl_cleandeleg(dp);
2900191783Srmacklem		(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2901191783Srmacklem		nfscl_freedeleg(&clp->nfsc_deleg, dp);
2902191783Srmacklem	}
2903191783Srmacklem	NFSFREECRED(cred);
2904191783Srmacklem}
2905191783Srmacklem
2906191783Srmacklem/*
2907191783Srmacklem * Do a callback RPC.
2908191783Srmacklem */
2909191783SrmacklemAPPLESTATIC void
2910191783Srmacklemnfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
2911191783Srmacklem{
2912191783Srmacklem	int i, op;
2913191783Srmacklem	u_int32_t *tl;
2914191783Srmacklem	struct nfsclclient *clp;
2915191783Srmacklem	struct nfscldeleg *dp = NULL;
2916191783Srmacklem	int numops, taglen = -1, error = 0, trunc, ret = 0;
2917191783Srmacklem	u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident;
2918191783Srmacklem	u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
2919191783Srmacklem	vnode_t vp = NULL;
2920191783Srmacklem	struct nfsnode *np;
2921191783Srmacklem	struct vattr va;
2922191783Srmacklem	struct nfsfh *nfhp;
2923191783Srmacklem	mount_t mp;
2924191783Srmacklem	nfsattrbit_t attrbits, rattrbits;
2925191783Srmacklem	nfsv4stateid_t stateid;
2926191783Srmacklem
2927191783Srmacklem	nfsrvd_rephead(nd);
2928191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2929191783Srmacklem	taglen = fxdr_unsigned(int, *tl);
2930191783Srmacklem	if (taglen < 0) {
2931191783Srmacklem		error = EBADRPC;
2932191783Srmacklem		goto nfsmout;
2933191783Srmacklem	}
2934191783Srmacklem	if (taglen <= NFSV4_SMALLSTR)
2935191783Srmacklem		tagstr = tag;
2936191783Srmacklem	else
2937191783Srmacklem		tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
2938191783Srmacklem	error = nfsrv_mtostr(nd, tagstr, taglen);
2939191783Srmacklem	if (error) {
2940191783Srmacklem		if (taglen > NFSV4_SMALLSTR)
2941191783Srmacklem			free(tagstr, M_TEMP);
2942191783Srmacklem		taglen = -1;
2943191783Srmacklem		goto nfsmout;
2944191783Srmacklem	}
2945191783Srmacklem	(void) nfsm_strtom(nd, tag, taglen);
2946191783Srmacklem	if (taglen > NFSV4_SMALLSTR) {
2947191783Srmacklem		free(tagstr, M_TEMP);
2948191783Srmacklem	}
2949191783Srmacklem	NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
2950191783Srmacklem	NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
2951191783Srmacklem	minorvers = fxdr_unsigned(u_int32_t, *tl++);
2952191783Srmacklem	if (minorvers != NFSV4_MINORVERSION)
2953191783Srmacklem		nd->nd_repstat = NFSERR_MINORVERMISMATCH;
2954191783Srmacklem	cbident = fxdr_unsigned(u_int32_t, *tl++);
2955191783Srmacklem	if (nd->nd_repstat)
2956191783Srmacklem		numops = 0;
2957191783Srmacklem	else
2958191783Srmacklem		numops = fxdr_unsigned(int, *tl);
2959191783Srmacklem	/*
2960191783Srmacklem	 * Loop around doing the sub ops.
2961191783Srmacklem	 */
2962191783Srmacklem	for (i = 0; i < numops; i++) {
2963191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2964191783Srmacklem		NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
2965191783Srmacklem		*repp++ = *tl;
2966191783Srmacklem		op = fxdr_unsigned(int, *tl);
2967191783Srmacklem		if (op < NFSV4OP_CBGETATTR || op > NFSV4OP_CBRECALL) {
2968191783Srmacklem		    nd->nd_repstat = NFSERR_OPILLEGAL;
2969191783Srmacklem		    *repp = nfscl_errmap(nd);
2970191783Srmacklem		    retops++;
2971191783Srmacklem		    break;
2972191783Srmacklem		}
2973191783Srmacklem		nd->nd_procnum = op;
2974191783Srmacklem		newnfsstats.cbrpccnt[nd->nd_procnum]++;
2975191783Srmacklem		switch (op) {
2976191783Srmacklem		case NFSV4OP_CBGETATTR:
2977191783Srmacklem			clp = NULL;
2978191783Srmacklem			error = nfsm_getfh(nd, &nfhp);
2979191783Srmacklem			if (!error)
2980191783Srmacklem				error = nfsrv_getattrbits(nd, &attrbits,
2981191783Srmacklem				    NULL, NULL);
2982191783Srmacklem			if (!error) {
2983191783Srmacklem				mp = nfscl_getmnt(cbident);
2984191783Srmacklem				if (mp == NULL)
2985191783Srmacklem					error = NFSERR_SERVERFAULT;
2986191783Srmacklem			}
2987191783Srmacklem			if (!error) {
2988191783Srmacklem				dp = NULL;
2989191783Srmacklem				NFSLOCKCLSTATE();
2990191783Srmacklem				clp = nfscl_findcl(VFSTONFS(mp));
2991191783Srmacklem				if (clp != NULL)
2992191783Srmacklem					dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
2993191783Srmacklem					    nfhp->nfh_len);
2994191783Srmacklem				NFSUNLOCKCLSTATE();
2995191783Srmacklem				if (dp == NULL)
2996191783Srmacklem					error = NFSERR_SERVERFAULT;
2997191783Srmacklem			}
2998191783Srmacklem			if (!error) {
2999191783Srmacklem				ret = nfscl_ngetreopen(mp, nfhp->nfh_fh,
3000191783Srmacklem				    nfhp->nfh_len, p, &np);
3001191783Srmacklem				if (!ret)
3002191783Srmacklem					vp = NFSTOV(np);
3003191783Srmacklem			}
3004191783Srmacklem			if (nfhp != NULL)
3005191783Srmacklem				FREE((caddr_t)nfhp, M_NFSFH);
3006191783Srmacklem			if (!error) {
3007191783Srmacklem				NFSZERO_ATTRBIT(&rattrbits);
3008191783Srmacklem				if (NFSISSET_ATTRBIT(&attrbits,
3009191783Srmacklem				    NFSATTRBIT_SIZE)) {
3010191783Srmacklem					if (!ret)
3011191783Srmacklem						va.va_size = np->n_size;
3012191783Srmacklem					else
3013191783Srmacklem						va.va_size = dp->nfsdl_size;
3014191783Srmacklem					NFSSETBIT_ATTRBIT(&rattrbits,
3015191783Srmacklem					    NFSATTRBIT_SIZE);
3016191783Srmacklem				}
3017191783Srmacklem				if (NFSISSET_ATTRBIT(&attrbits,
3018191783Srmacklem				    NFSATTRBIT_CHANGE)) {
3019191783Srmacklem					va.va_filerev = dp->nfsdl_change;
3020191783Srmacklem					if (ret || (np->n_flag & NDELEGMOD))
3021191783Srmacklem						va.va_filerev++;
3022191783Srmacklem					NFSSETBIT_ATTRBIT(&rattrbits,
3023191783Srmacklem					    NFSATTRBIT_CHANGE);
3024191783Srmacklem				}
3025191783Srmacklem				(void) nfsv4_fillattr(nd, NULL, NULL, &va,
3026191783Srmacklem				    NULL, 0, &rattrbits, NULL, NULL, 0, 0);
3027191783Srmacklem				if (!ret)
3028191783Srmacklem					vrele(vp);
3029191783Srmacklem			}
3030191783Srmacklem			break;
3031191783Srmacklem		case NFSV4OP_CBRECALL:
3032191783Srmacklem			clp = NULL;
3033191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
3034191783Srmacklem			    NFSX_UNSIGNED);
3035191783Srmacklem			stateid.seqid = *tl++;
3036191783Srmacklem			NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
3037191783Srmacklem			    NFSX_STATEIDOTHER);
3038191783Srmacklem			tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3039191783Srmacklem			trunc = fxdr_unsigned(int, *tl);
3040191783Srmacklem			error = nfsm_getfh(nd, &nfhp);
3041191783Srmacklem			if (!error) {
3042191783Srmacklem				mp = nfscl_getmnt(cbident);
3043191783Srmacklem				if (mp == NULL)
3044191783Srmacklem					error = NFSERR_SERVERFAULT;
3045191783Srmacklem			}
3046191783Srmacklem			if (!error) {
3047191783Srmacklem				NFSLOCKCLSTATE();
3048191783Srmacklem				clp = nfscl_findcl(VFSTONFS(mp));
3049191783Srmacklem				if (clp != NULL) {
3050191783Srmacklem					dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3051191783Srmacklem					    nfhp->nfh_len);
3052191783Srmacklem					if (dp != NULL) {
3053191783Srmacklem						dp->nfsdl_flags |=
3054191783Srmacklem						    NFSCLDL_RECALL;
3055191783Srmacklem						wakeup((caddr_t)clp);
3056191783Srmacklem					}
3057191783Srmacklem				} else {
3058191783Srmacklem					error = NFSERR_SERVERFAULT;
3059191783Srmacklem				}
3060191783Srmacklem				NFSUNLOCKCLSTATE();
3061191783Srmacklem			}
3062191783Srmacklem			if (nfhp != NULL)
3063191783Srmacklem				FREE((caddr_t)nfhp, M_NFSFH);
3064191783Srmacklem			break;
3065191783Srmacklem		};
3066191783Srmacklem		if (error) {
3067191783Srmacklem			if (error == EBADRPC || error == NFSERR_BADXDR) {
3068191783Srmacklem				nd->nd_repstat = NFSERR_BADXDR;
3069191783Srmacklem			} else {
3070191783Srmacklem				nd->nd_repstat = error;
3071191783Srmacklem			}
3072191783Srmacklem			error = 0;
3073191783Srmacklem		}
3074191783Srmacklem		retops++;
3075191783Srmacklem		if (nd->nd_repstat) {
3076191783Srmacklem			*repp = nfscl_errmap(nd);
3077191783Srmacklem			break;
3078191783Srmacklem		} else
3079191783Srmacklem			*repp = 0;	/* NFS4_OK */
3080191783Srmacklem	}
3081191783Srmacklemnfsmout:
3082191783Srmacklem	if (error) {
3083191783Srmacklem		if (error == EBADRPC || error == NFSERR_BADXDR)
3084191783Srmacklem			nd->nd_repstat = NFSERR_BADXDR;
3085191783Srmacklem		else
3086191783Srmacklem			printf("nfsv4 comperr1=%d\n", error);
3087191783Srmacklem	}
3088191783Srmacklem	if (taglen == -1) {
3089191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3090191783Srmacklem		*tl++ = 0;
3091191783Srmacklem		*tl = 0;
3092191783Srmacklem	} else {
3093191783Srmacklem		*retopsp = txdr_unsigned(retops);
3094191783Srmacklem	}
3095191783Srmacklem	*nd->nd_errp = nfscl_errmap(nd);
3096191783Srmacklem}
3097191783Srmacklem
3098191783Srmacklem/*
3099191783Srmacklem * Generate the next cbident value. Basically just increment a static value
3100191783Srmacklem * and then check that it isn't already in the list, if it has wrapped around.
3101191783Srmacklem */
3102191783Srmacklemstatic u_int32_t
3103191783Srmacklemnfscl_nextcbident(void)
3104191783Srmacklem{
3105191783Srmacklem	struct nfsclclient *clp;
3106191783Srmacklem	int matched;
3107191783Srmacklem	static u_int32_t nextcbident = 0;
3108191783Srmacklem	static int haswrapped = 0;
3109191783Srmacklem
3110191783Srmacklem	nextcbident++;
3111191783Srmacklem	if (nextcbident == 0)
3112191783Srmacklem		haswrapped = 1;
3113191783Srmacklem	if (haswrapped) {
3114191783Srmacklem		/*
3115191783Srmacklem		 * Search the clientid list for one already using this cbident.
3116191783Srmacklem		 */
3117191783Srmacklem		do {
3118191783Srmacklem			matched = 0;
3119191783Srmacklem			NFSLOCKCLSTATE();
3120191783Srmacklem			LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3121191783Srmacklem				if (clp->nfsc_cbident == nextcbident) {
3122191783Srmacklem					matched = 1;
3123191783Srmacklem					break;
3124191783Srmacklem				}
3125191783Srmacklem			}
3126191783Srmacklem			NFSUNLOCKCLSTATE();
3127191783Srmacklem			if (matched == 1)
3128191783Srmacklem				nextcbident++;
3129191783Srmacklem		} while (matched);
3130191783Srmacklem	}
3131191783Srmacklem	return (nextcbident);
3132191783Srmacklem}
3133191783Srmacklem
3134191783Srmacklem/*
3135191783Srmacklem * Get the mount point related to a given cbident.
3136191783Srmacklem */
3137191783Srmacklemstatic mount_t
3138191783Srmacklemnfscl_getmnt(u_int32_t cbident)
3139191783Srmacklem{
3140191783Srmacklem	struct nfsclclient *clp;
3141191783Srmacklem	struct nfsmount *nmp;
3142191783Srmacklem
3143191783Srmacklem	NFSLOCKCLSTATE();
3144191783Srmacklem	LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3145191783Srmacklem		if (clp->nfsc_cbident == cbident)
3146191783Srmacklem			break;
3147191783Srmacklem	}
3148191783Srmacklem	if (clp == NULL) {
3149191783Srmacklem		NFSUNLOCKCLSTATE();
3150191783Srmacklem		return (NULL);
3151191783Srmacklem	}
3152191783Srmacklem	nmp = clp->nfsc_nmp;
3153191783Srmacklem	NFSUNLOCKCLSTATE();
3154191783Srmacklem	return (nmp->nm_mountp);
3155191783Srmacklem}
3156191783Srmacklem
3157191783Srmacklem/*
3158191783Srmacklem * Search for a lock conflict locally on the client. A conflict occurs if
3159191783Srmacklem * - not same owner and overlapping byte range and at least one of them is
3160191783Srmacklem *   a write lock or this is an unlock.
3161191783Srmacklem */
3162191783Srmacklemstatic int
3163201439Srmacklemnfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
3164201439Srmacklem    struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
3165201439Srmacklem    struct nfscllock **lopp)
3166191783Srmacklem{
3167191783Srmacklem	struct nfsclowner *owp;
3168191783Srmacklem	struct nfsclopen *op;
3169191783Srmacklem	int ret;
3170191783Srmacklem
3171191783Srmacklem	if (dp != NULL) {
3172191783Srmacklem		ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
3173191783Srmacklem		if (ret)
3174191783Srmacklem			return (ret);
3175191783Srmacklem	}
3176191783Srmacklem	LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3177191783Srmacklem		LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3178201439Srmacklem			if (op->nfso_fhlen == fhlen &&
3179201439Srmacklem			    !NFSBCMP(op->nfso_fh, fhp, fhlen)) {
3180201439Srmacklem				ret = nfscl_checkconflict(&op->nfso_lock, nlop,
3181201439Srmacklem				    own, lopp);
3182201439Srmacklem				if (ret)
3183201439Srmacklem					return (ret);
3184201439Srmacklem			}
3185191783Srmacklem		}
3186191783Srmacklem	}
3187191783Srmacklem	return (0);
3188191783Srmacklem}
3189191783Srmacklem
3190191783Srmacklemstatic int
3191191783Srmacklemnfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
3192191783Srmacklem    u_int8_t *own, struct nfscllock **lopp)
3193191783Srmacklem{
3194191783Srmacklem	struct nfscllockowner *lp;
3195191783Srmacklem	struct nfscllock *lop;
3196191783Srmacklem
3197191783Srmacklem	LIST_FOREACH(lp, lhp, nfsl_list) {
3198191783Srmacklem		if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
3199191783Srmacklem			LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3200191783Srmacklem				if (lop->nfslo_first >= nlop->nfslo_end)
3201191783Srmacklem					break;
3202191783Srmacklem				if (lop->nfslo_end <= nlop->nfslo_first)
3203191783Srmacklem					continue;
3204191783Srmacklem				if (lop->nfslo_type == F_WRLCK ||
3205191783Srmacklem				    nlop->nfslo_type == F_WRLCK ||
3206191783Srmacklem				    nlop->nfslo_type == F_UNLCK) {
3207191783Srmacklem					if (lopp != NULL)
3208191783Srmacklem						*lopp = lop;
3209191783Srmacklem					return (NFSERR_DENIED);
3210191783Srmacklem				}
3211191783Srmacklem			}
3212191783Srmacklem		}
3213191783Srmacklem	}
3214191783Srmacklem	return (0);
3215191783Srmacklem}
3216191783Srmacklem
3217191783Srmacklem/*
3218191783Srmacklem * Check for a local conflicting lock.
3219191783Srmacklem */
3220191783SrmacklemAPPLESTATIC int
3221191783Srmacklemnfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
3222191783Srmacklem    u_int64_t len, struct flock *fl, NFSPROC_T *p)
3223191783Srmacklem{
3224191783Srmacklem	struct nfscllock *lop, nlck;
3225191783Srmacklem	struct nfscldeleg *dp;
3226191783Srmacklem	struct nfsnode *np;
3227191783Srmacklem	u_int8_t own[NFSV4CL_LOCKNAMELEN];
3228191783Srmacklem	int error;
3229191783Srmacklem
3230191783Srmacklem	nlck.nfslo_type = fl->l_type;
3231191783Srmacklem	nlck.nfslo_first = off;
3232191783Srmacklem	if (len == NFS64BITSSET) {
3233191783Srmacklem		nlck.nfslo_end = NFS64BITSSET;
3234191783Srmacklem	} else {
3235191783Srmacklem		nlck.nfslo_end = off + len;
3236191783Srmacklem		if (nlck.nfslo_end <= nlck.nfslo_first)
3237191783Srmacklem			return (NFSERR_INVAL);
3238191783Srmacklem	}
3239191783Srmacklem	np = VTONFS(vp);
3240191783Srmacklem	nfscl_filllockowner(p, own);
3241191783Srmacklem	NFSLOCKCLSTATE();
3242191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3243201439Srmacklem	error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
3244201439Srmacklem	    &nlck, own, dp, &lop);
3245201439Srmacklem	if (error != 0) {
3246191783Srmacklem		fl->l_whence = SEEK_SET;
3247191783Srmacklem		fl->l_start = lop->nfslo_first;
3248191783Srmacklem		if (lop->nfslo_end == NFS64BITSSET)
3249191783Srmacklem			fl->l_len = 0;
3250191783Srmacklem		else
3251191783Srmacklem			fl->l_len = lop->nfslo_end - lop->nfslo_first;
3252191783Srmacklem		fl->l_pid = (pid_t)0;
3253191783Srmacklem		fl->l_type = lop->nfslo_type;
3254201439Srmacklem		error = -1;			/* no RPC required */
3255191783Srmacklem	} else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
3256191783Srmacklem	    fl->l_type == F_RDLCK)) {
3257191783Srmacklem		/*
3258191783Srmacklem		 * The delegation ensures that there isn't a conflicting
3259191783Srmacklem		 * lock on the server, so return -1 to indicate an RPC
3260191783Srmacklem		 * isn't required.
3261191783Srmacklem		 */
3262191783Srmacklem		fl->l_type = F_UNLCK;
3263191783Srmacklem		error = -1;
3264191783Srmacklem	}
3265191783Srmacklem	NFSUNLOCKCLSTATE();
3266191783Srmacklem	return (error);
3267191783Srmacklem}
3268191783Srmacklem
3269191783Srmacklem/*
3270191783Srmacklem * Handle Recall of a delegation.
3271191783Srmacklem * The clp must be exclusive locked when this is called.
3272191783Srmacklem */
3273191783Srmacklemstatic int
3274191783Srmacklemnfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
3275191783Srmacklem    struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p)
3276191783Srmacklem{
3277191783Srmacklem	struct nfsclowner *owp, *lowp, *nowp;
3278191783Srmacklem	struct nfsclopen *op, *lop;
3279191783Srmacklem	struct nfscllockowner *lp;
3280191783Srmacklem	struct nfscllock *lckp;
3281191783Srmacklem	struct nfsnode *np;
3282191783Srmacklem	int error = 0, ret, gotvp = 0;
3283191783Srmacklem
3284191783Srmacklem	if (vp == NULL) {
3285191783Srmacklem		/*
3286191783Srmacklem		 * First, get a vnode for the file. This is needed to do RPCs.
3287191783Srmacklem		 */
3288191783Srmacklem		ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
3289191783Srmacklem		    dp->nfsdl_fhlen, p, &np);
3290191783Srmacklem		if (ret) {
3291191783Srmacklem			/*
3292191783Srmacklem			 * File isn't open, so nothing to move over to the
3293191783Srmacklem			 * server.
3294191783Srmacklem			 */
3295191783Srmacklem			return (0);
3296191783Srmacklem		}
3297191783Srmacklem		vp = NFSTOV(np);
3298191783Srmacklem		gotvp = 1;
3299191783Srmacklem	} else {
3300191783Srmacklem		np = VTONFS(vp);
3301191783Srmacklem	}
3302191783Srmacklem	dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
3303191783Srmacklem	NFSINVALATTRCACHE(np);
3304191783Srmacklem
3305191783Srmacklem	/*
3306191783Srmacklem	 * Ok, if it's a write delegation, flush data to the server, so
3307191783Srmacklem	 * that close/open consistency is retained.
3308191783Srmacklem	 */
3309191783Srmacklem	NFSLOCKNODE(np);
3310191783Srmacklem	if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
3311191783Srmacklem#ifdef APPLE
3312191783Srmacklem		OSBitOrAtomic((u_int32_t)NDELEGRECALL, (UInt32 *)&np->n_flag);
3313191783Srmacklem#else
3314191783Srmacklem		np->n_flag |= NDELEGRECALL;
3315191783Srmacklem#endif
3316191783Srmacklem		NFSUNLOCKNODE(np);
3317191783Srmacklem		(void) ncl_flush(vp, MNT_WAIT, cred, p, 1);
3318191783Srmacklem		NFSLOCKNODE(np);
3319191783Srmacklem#ifdef APPLE
3320191783Srmacklem		OSBitAndAtomic((int32_t)~(NMODIFIED | NDELEGRECALL), (UInt32 *)&np->n_flag);
3321191783Srmacklem#else
3322191783Srmacklem		np->n_flag &= ~(NMODIFIED | NDELEGRECALL);
3323191783Srmacklem#endif
3324191783Srmacklem	}
3325191783Srmacklem	NFSUNLOCKNODE(np);
3326191783Srmacklem
3327191783Srmacklem	/*
3328191783Srmacklem	 * Now, for each openowner with opens issued locally, move them
3329191783Srmacklem	 * over to state against the server.
3330191783Srmacklem	 */
3331191783Srmacklem	LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
3332191783Srmacklem		lop = LIST_FIRST(&lowp->nfsow_open);
3333191783Srmacklem		if (lop != NULL) {
3334191783Srmacklem			if (LIST_NEXT(lop, nfso_list) != NULL)
3335191783Srmacklem				panic("nfsdlg mult opens");
3336191783Srmacklem			/*
3337191783Srmacklem			 * Look for the same openowner against the server.
3338191783Srmacklem			 */
3339191783Srmacklem			LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3340191783Srmacklem				if (!NFSBCMP(lowp->nfsow_owner,
3341191783Srmacklem				    owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
3342191783Srmacklem					newnfs_copycred(&dp->nfsdl_cred, cred);
3343191783Srmacklem					ret = nfscl_moveopen(vp, clp, nmp, lop,
3344191783Srmacklem					    owp, dp, cred, p);
3345191783Srmacklem					if (ret == NFSERR_STALECLIENTID ||
3346191783Srmacklem					    ret == NFSERR_STALEDONTRECOVER) {
3347191783Srmacklem						if (gotvp)
3348191783Srmacklem							vrele(vp);
3349191783Srmacklem						return (ret);
3350191783Srmacklem					}
3351191783Srmacklem					if (ret) {
3352191783Srmacklem						nfscl_freeopen(lop, 1);
3353191783Srmacklem						if (!error)
3354191783Srmacklem							error = ret;
3355191783Srmacklem					}
3356191783Srmacklem					break;
3357191783Srmacklem				}
3358191783Srmacklem			}
3359191783Srmacklem
3360191783Srmacklem			/*
3361191783Srmacklem			 * If no openowner found, create one and get an open
3362191783Srmacklem			 * for it.
3363191783Srmacklem			 */
3364191783Srmacklem			if (owp == NULL) {
3365191783Srmacklem				MALLOC(nowp, struct nfsclowner *,
3366191783Srmacklem				    sizeof (struct nfsclowner), M_NFSCLOWNER,
3367191783Srmacklem				    M_WAITOK);
3368191783Srmacklem				nfscl_newopen(clp, NULL, &owp, &nowp, &op,
3369191783Srmacklem				    NULL, lowp->nfsow_owner, dp->nfsdl_fh,
3370191783Srmacklem				    dp->nfsdl_fhlen, NULL);
3371191783Srmacklem				newnfs_copycred(&dp->nfsdl_cred, cred);
3372191783Srmacklem				ret = nfscl_moveopen(vp, clp, nmp, lop,
3373191783Srmacklem				    owp, dp, cred, p);
3374191783Srmacklem				if (ret) {
3375191783Srmacklem					nfscl_freeopenowner(owp, 0);
3376191783Srmacklem					if (ret == NFSERR_STALECLIENTID ||
3377191783Srmacklem					    ret == NFSERR_STALEDONTRECOVER) {
3378191783Srmacklem						if (gotvp)
3379191783Srmacklem							vrele(vp);
3380191783Srmacklem						return (ret);
3381191783Srmacklem					}
3382191783Srmacklem					if (ret) {
3383191783Srmacklem						nfscl_freeopen(lop, 1);
3384191783Srmacklem						if (!error)
3385191783Srmacklem							error = ret;
3386191783Srmacklem					}
3387191783Srmacklem				}
3388191783Srmacklem			}
3389191783Srmacklem		}
3390191783Srmacklem	}
3391191783Srmacklem
3392191783Srmacklem	/*
3393191783Srmacklem	 * Now, get byte range locks for any locks done locally.
3394191783Srmacklem	 */
3395191783Srmacklem	LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3396191783Srmacklem		LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
3397191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
3398191783Srmacklem			ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
3399191783Srmacklem			if (ret == NFSERR_STALESTATEID ||
3400191783Srmacklem			    ret == NFSERR_STALEDONTRECOVER ||
3401191783Srmacklem			    ret == NFSERR_STALECLIENTID) {
3402191783Srmacklem				if (gotvp)
3403191783Srmacklem					vrele(vp);
3404191783Srmacklem				return (ret);
3405191783Srmacklem			}
3406191783Srmacklem			if (ret && !error)
3407191783Srmacklem				error = ret;
3408191783Srmacklem		}
3409191783Srmacklem	}
3410191783Srmacklem	if (gotvp)
3411191783Srmacklem		vrele(vp);
3412191783Srmacklem	return (error);
3413191783Srmacklem}
3414191783Srmacklem
3415191783Srmacklem/*
3416191783Srmacklem * Move a locally issued open over to an owner on the state list.
3417191783Srmacklem * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
3418191783Srmacklem * returns with it unlocked.
3419191783Srmacklem */
3420191783Srmacklemstatic int
3421191783Srmacklemnfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3422191783Srmacklem    struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
3423191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
3424191783Srmacklem{
3425191783Srmacklem	struct nfsclopen *op, *nop;
3426191783Srmacklem	struct nfscldeleg *ndp;
3427191783Srmacklem	struct nfsnode *np;
3428191783Srmacklem	int error = 0, newone;
3429191783Srmacklem
3430191783Srmacklem	/*
3431191783Srmacklem	 * First, look for an appropriate open, If found, just increment the
3432191783Srmacklem	 * opencnt in it.
3433191783Srmacklem	 */
3434191783Srmacklem	LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3435191783Srmacklem		if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
3436191783Srmacklem		    op->nfso_fhlen == lop->nfso_fhlen &&
3437191783Srmacklem		    !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
3438191783Srmacklem			op->nfso_opencnt += lop->nfso_opencnt;
3439191783Srmacklem			nfscl_freeopen(lop, 1);
3440191783Srmacklem			return (0);
3441191783Srmacklem		}
3442191783Srmacklem	}
3443191783Srmacklem
3444191783Srmacklem	/* No appropriate open, so we have to do one against the server. */
3445191783Srmacklem	np = VTONFS(vp);
3446191783Srmacklem	MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
3447191783Srmacklem	    lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
3448191783Srmacklem	newone = 0;
3449191783Srmacklem	nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
3450191783Srmacklem	    lop->nfso_fh, lop->nfso_fhlen, &newone);
3451191783Srmacklem	ndp = dp;
3452191783Srmacklem	error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen,
3453191783Srmacklem	    lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
3454191783Srmacklem	    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
3455191783Srmacklem	if (error) {
3456191783Srmacklem		if (newone)
3457191783Srmacklem			nfscl_freeopen(op, 0);
3458191783Srmacklem	} else {
3459191783Srmacklem		if (newone)
3460191783Srmacklem			newnfs_copyincred(cred, &op->nfso_cred);
3461191783Srmacklem		op->nfso_mode |= lop->nfso_mode;
3462191783Srmacklem		op->nfso_opencnt += lop->nfso_opencnt;
3463191783Srmacklem		nfscl_freeopen(lop, 1);
3464191783Srmacklem	}
3465191783Srmacklem	if (nop != NULL)
3466191783Srmacklem		FREE((caddr_t)nop, M_NFSCLOPEN);
3467191783Srmacklem	if (ndp != NULL) {
3468191783Srmacklem		/*
3469191783Srmacklem		 * What should I do with the returned delegation, since the
3470191783Srmacklem		 * delegation is being recalled? For now, just printf and
3471191783Srmacklem		 * through it away.
3472191783Srmacklem		 */
3473191783Srmacklem		printf("Moveopen returned deleg\n");
3474191783Srmacklem		FREE((caddr_t)ndp, M_NFSCLDELEG);
3475191783Srmacklem	}
3476191783Srmacklem	return (error);
3477191783Srmacklem}
3478191783Srmacklem
3479191783Srmacklem/*
3480191783Srmacklem * Recall all delegations on this client.
3481191783Srmacklem */
3482191783Srmacklemstatic void
3483191783Srmacklemnfscl_totalrecall(struct nfsclclient *clp)
3484191783Srmacklem{
3485191783Srmacklem	struct nfscldeleg *dp;
3486191783Srmacklem
3487191783Srmacklem	TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
3488191783Srmacklem		dp->nfsdl_flags |= NFSCLDL_RECALL;
3489191783Srmacklem}
3490191783Srmacklem
3491191783Srmacklem/*
3492191783Srmacklem * Relock byte ranges. Called for delegation recall and state expiry.
3493191783Srmacklem */
3494191783Srmacklemstatic int
3495191783Srmacklemnfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3496191783Srmacklem    struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
3497191783Srmacklem    NFSPROC_T *p)
3498191783Srmacklem{
3499191783Srmacklem	struct nfscllockowner *nlp;
3500191783Srmacklem	struct nfsfh *nfhp;
3501191783Srmacklem	u_int64_t off, len;
3502191783Srmacklem	u_int32_t clidrev = 0;
3503191783Srmacklem	int error, newone, donelocally;
3504191783Srmacklem
3505191783Srmacklem	off = lop->nfslo_first;
3506191783Srmacklem	len = lop->nfslo_end - lop->nfslo_first;
3507191783Srmacklem	error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
3508191783Srmacklem	    clp, 1, lp->nfsl_owner, lp->nfsl_openowner, &nlp, &newone,
3509191783Srmacklem	    &donelocally);
3510191783Srmacklem	if (error || donelocally)
3511191783Srmacklem		return (error);
3512191783Srmacklem	if (nmp->nm_clp != NULL)
3513191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
3514191783Srmacklem	else
3515191783Srmacklem		clidrev = 0;
3516191783Srmacklem	nfhp = VTONFS(vp)->n_fhp;
3517191783Srmacklem	error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
3518191783Srmacklem	    nfhp->nfh_len, nlp, newone, 0, off,
3519191783Srmacklem	    len, lop->nfslo_type, cred, p);
3520191783Srmacklem	if (error)
3521191783Srmacklem		nfscl_freelockowner(nlp, 0);
3522191783Srmacklem	return (error);
3523191783Srmacklem}
3524191783Srmacklem
3525191783Srmacklem/*
3526191783Srmacklem * Called to re-open a file. Basically get a vnode for the file handle
3527191783Srmacklem * and then call nfsrpc_openrpc() to do the rest.
3528191783Srmacklem */
3529191783Srmacklemstatic int
3530191783Srmacklemnfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
3531191783Srmacklem    u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
3532191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
3533191783Srmacklem{
3534191783Srmacklem	struct nfsnode *np;
3535191783Srmacklem	vnode_t vp;
3536191783Srmacklem	int error;
3537191783Srmacklem
3538191783Srmacklem	error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
3539191783Srmacklem	if (error)
3540191783Srmacklem		return (error);
3541191783Srmacklem	vp = NFSTOV(np);
3542191783Srmacklem	if (np->n_v4 != NULL) {
3543191783Srmacklem		error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
3544191783Srmacklem		    np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
3545191783Srmacklem		    NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
3546191783Srmacklem		    cred, p);
3547191783Srmacklem	} else {
3548191783Srmacklem		error = EINVAL;
3549191783Srmacklem	}
3550191783Srmacklem	vrele(vp);
3551191783Srmacklem	return (error);
3552191783Srmacklem}
3553191783Srmacklem
3554191783Srmacklem/*
3555191783Srmacklem * Try an open against the server. Just call nfsrpc_openrpc(), retrying while
3556191783Srmacklem * NFSERR_DELAY. Also, try system credentials, if the passed in credentials
3557191783Srmacklem * fail.
3558191783Srmacklem */
3559191783Srmacklemstatic int
3560191783Srmacklemnfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
3561191783Srmacklem    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
3562191783Srmacklem    u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
3563191783Srmacklem    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
3564191783Srmacklem{
3565191783Srmacklem	int error;
3566191783Srmacklem
3567191783Srmacklem	do {
3568191783Srmacklem		error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
3569191783Srmacklem		    mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
3570191783Srmacklem		    0, 0);
3571191783Srmacklem		if (error == NFSERR_DELAY)
3572191783Srmacklem			(void) nfs_catnap(PZERO, "nfstryop");
3573191783Srmacklem	} while (error == NFSERR_DELAY);
3574191783Srmacklem	if (error == EAUTH || error == EACCES) {
3575191783Srmacklem		/* Try again using system credentials */
3576191783Srmacklem		newnfs_setroot(cred);
3577191783Srmacklem		do {
3578191783Srmacklem		    error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
3579191783Srmacklem			newfhlen, mode, op, name, namelen, ndpp, reclaim,
3580191783Srmacklem			delegtype, cred, p, 1, 0);
3581191783Srmacklem		    if (error == NFSERR_DELAY)
3582191783Srmacklem			(void) nfs_catnap(PZERO, "nfstryop");
3583191783Srmacklem		} while (error == NFSERR_DELAY);
3584191783Srmacklem	}
3585191783Srmacklem	return (error);
3586191783Srmacklem}
3587191783Srmacklem
3588191783Srmacklem/*
3589191783Srmacklem * Try a byte range lock. Just loop on nfsrpc_lock() while it returns
3590191783Srmacklem * NFSERR_DELAY. Also, retry with system credentials, if the provided
3591191783Srmacklem * cred don't work.
3592191783Srmacklem */
3593191783Srmacklemstatic int
3594191783Srmacklemnfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
3595191783Srmacklem    int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
3596191783Srmacklem    u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
3597191783Srmacklem{
3598191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3599191783Srmacklem	int error;
3600191783Srmacklem
3601191783Srmacklem	do {
3602191783Srmacklem		error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
3603191783Srmacklem		    reclaim, off, len, type, cred, p, 0);
3604191783Srmacklem		if (!error && nd->nd_repstat == NFSERR_DELAY)
3605191783Srmacklem			(void) nfs_catnap(PZERO, "nfstrylck");
3606191783Srmacklem	} while (!error && nd->nd_repstat == NFSERR_DELAY);
3607191783Srmacklem	if (!error)
3608191783Srmacklem		error = nd->nd_repstat;
3609191783Srmacklem	if (error == EAUTH || error == EACCES) {
3610191783Srmacklem		/* Try again using root credentials */
3611191783Srmacklem		newnfs_setroot(cred);
3612191783Srmacklem		do {
3613191783Srmacklem			error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
3614191783Srmacklem			    newone, reclaim, off, len, type, cred, p, 1);
3615191783Srmacklem			if (!error && nd->nd_repstat == NFSERR_DELAY)
3616191783Srmacklem				(void) nfs_catnap(PZERO, "nfstrylck");
3617191783Srmacklem		} while (!error && nd->nd_repstat == NFSERR_DELAY);
3618191783Srmacklem		if (!error)
3619191783Srmacklem			error = nd->nd_repstat;
3620191783Srmacklem	}
3621191783Srmacklem	return (error);
3622191783Srmacklem}
3623191783Srmacklem
3624191783Srmacklem/*
3625191783Srmacklem * Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
3626191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
3627191783Srmacklem * credentials fail.
3628191783Srmacklem */
3629191783Srmacklemstatic int
3630191783Srmacklemnfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
3631191783Srmacklem    struct nfsmount *nmp, NFSPROC_T *p)
3632191783Srmacklem{
3633191783Srmacklem	int error;
3634191783Srmacklem
3635191783Srmacklem	do {
3636191783Srmacklem		error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
3637191783Srmacklem		if (error == NFSERR_DELAY)
3638191783Srmacklem			(void) nfs_catnap(PZERO, "nfstrydp");
3639191783Srmacklem	} while (error == NFSERR_DELAY);
3640191783Srmacklem	if (error == EAUTH || error == EACCES) {
3641191783Srmacklem		/* Try again using system credentials */
3642191783Srmacklem		newnfs_setroot(cred);
3643191783Srmacklem		do {
3644191783Srmacklem			error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
3645191783Srmacklem			if (error == NFSERR_DELAY)
3646191783Srmacklem				(void) nfs_catnap(PZERO, "nfstrydp");
3647191783Srmacklem		} while (error == NFSERR_DELAY);
3648191783Srmacklem	}
3649191783Srmacklem	return (error);
3650191783Srmacklem}
3651191783Srmacklem
3652191783Srmacklem/*
3653191783Srmacklem * Try a close against the server. Just call nfsrpc_closerpc(),
3654191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
3655191783Srmacklem * credentials fail.
3656191783Srmacklem */
3657191783SrmacklemAPPLESTATIC int
3658191783Srmacklemnfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
3659191783Srmacklem    struct nfsmount *nmp, NFSPROC_T *p)
3660191783Srmacklem{
3661191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3662191783Srmacklem	int error;
3663191783Srmacklem
3664191783Srmacklem	do {
3665191783Srmacklem		error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
3666191783Srmacklem		if (error == NFSERR_DELAY)
3667191783Srmacklem			(void) nfs_catnap(PZERO, "nfstrycl");
3668191783Srmacklem	} while (error == NFSERR_DELAY);
3669191783Srmacklem	if (error == EAUTH || error == EACCES) {
3670191783Srmacklem		/* Try again using system credentials */
3671191783Srmacklem		newnfs_setroot(cred);
3672191783Srmacklem		do {
3673191783Srmacklem			error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
3674191783Srmacklem			if (error == NFSERR_DELAY)
3675191783Srmacklem				(void) nfs_catnap(PZERO, "nfstrycl");
3676191783Srmacklem		} while (error == NFSERR_DELAY);
3677191783Srmacklem	}
3678191783Srmacklem	return (error);
3679191783Srmacklem}
3680191783Srmacklem
3681191783Srmacklem/*
3682191783Srmacklem * Decide if a delegation on a file permits close without flushing writes
3683191783Srmacklem * to the server. This might be a big performance win in some environments.
3684191783Srmacklem * (Not useful until the client does caching on local stable storage.)
3685191783Srmacklem */
3686191783SrmacklemAPPLESTATIC int
3687191783Srmacklemnfscl_mustflush(vnode_t vp)
3688191783Srmacklem{
3689191783Srmacklem	struct nfsclclient *clp;
3690191783Srmacklem	struct nfscldeleg *dp;
3691191783Srmacklem	struct nfsnode *np;
3692191783Srmacklem	struct nfsmount *nmp;
3693191783Srmacklem
3694191783Srmacklem	np = VTONFS(vp);
3695191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
3696191783Srmacklem	if (!NFSHASNFSV4(nmp))
3697191783Srmacklem		return (1);
3698191783Srmacklem	NFSLOCKCLSTATE();
3699191783Srmacklem	clp = nfscl_findcl(nmp);
3700191783Srmacklem	if (clp == NULL) {
3701191783Srmacklem		NFSUNLOCKCLSTATE();
3702191783Srmacklem		return (1);
3703191783Srmacklem	}
3704191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3705191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_RECALL))
3706191783Srmacklem	     == NFSCLDL_WRITE &&
3707191783Srmacklem	    (dp->nfsdl_sizelimit >= np->n_size ||
3708191783Srmacklem	     !NFSHASSTRICT3530(nmp))) {
3709191783Srmacklem		NFSUNLOCKCLSTATE();
3710191783Srmacklem		return (0);
3711191783Srmacklem	}
3712191783Srmacklem	NFSUNLOCKCLSTATE();
3713191783Srmacklem	return (1);
3714191783Srmacklem}
3715191783Srmacklem
3716191783Srmacklem/*
3717191783Srmacklem * See if a (write) delegation exists for this file.
3718191783Srmacklem */
3719191783SrmacklemAPPLESTATIC int
3720191783Srmacklemnfscl_nodeleg(vnode_t vp, int writedeleg)
3721191783Srmacklem{
3722191783Srmacklem	struct nfsclclient *clp;
3723191783Srmacklem	struct nfscldeleg *dp;
3724191783Srmacklem	struct nfsnode *np;
3725191783Srmacklem	struct nfsmount *nmp;
3726191783Srmacklem
3727191783Srmacklem	np = VTONFS(vp);
3728191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
3729191783Srmacklem	if (!NFSHASNFSV4(nmp))
3730191783Srmacklem		return (1);
3731191783Srmacklem	NFSLOCKCLSTATE();
3732191783Srmacklem	clp = nfscl_findcl(nmp);
3733191783Srmacklem	if (clp == NULL) {
3734191783Srmacklem		NFSUNLOCKCLSTATE();
3735191783Srmacklem		return (1);
3736191783Srmacklem	}
3737191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3738191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_RECALL) == 0 &&
3739191783Srmacklem	    (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE)
3740191783Srmacklem	     == NFSCLDL_WRITE)) {
3741191783Srmacklem		NFSUNLOCKCLSTATE();
3742191783Srmacklem		return (0);
3743191783Srmacklem	}
3744191783Srmacklem	NFSUNLOCKCLSTATE();
3745191783Srmacklem	return (1);
3746191783Srmacklem}
3747191783Srmacklem
3748191783Srmacklem/*
3749191783Srmacklem * Look for an associated delegation that should be DelegReturned.
3750191783Srmacklem */
3751191783SrmacklemAPPLESTATIC int
3752191783Srmacklemnfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
3753191783Srmacklem{
3754191783Srmacklem	struct nfsclclient *clp;
3755191783Srmacklem	struct nfscldeleg *dp;
3756191783Srmacklem	struct nfsclowner *owp;
3757191783Srmacklem	struct nfscllockowner *lp;
3758191783Srmacklem	struct nfsmount *nmp;
3759191783Srmacklem	struct ucred *cred;
3760191783Srmacklem	struct nfsnode *np;
3761191783Srmacklem	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
3762191783Srmacklem
3763191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
3764191783Srmacklem	np = VTONFS(vp);
3765191783Srmacklem	NFSLOCKCLSTATE();
3766191783Srmacklem	/*
3767191783Srmacklem	 * Loop around waiting for:
3768191783Srmacklem	 * - outstanding I/O operations on delegations to complete
3769191783Srmacklem	 * - for a delegation on vp that has state, lock the client and
3770191783Srmacklem	 *   do a recall
3771191783Srmacklem	 * - return delegation with no state
3772191783Srmacklem	 */
3773191783Srmacklem	while (1) {
3774191783Srmacklem		clp = nfscl_findcl(nmp);
3775191783Srmacklem		if (clp == NULL) {
3776191783Srmacklem			NFSUNLOCKCLSTATE();
3777191783Srmacklem			return (retcnt);
3778191783Srmacklem		}
3779191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
3780191783Srmacklem		    np->n_fhp->nfh_len);
3781191783Srmacklem		if (dp != NULL) {
3782191783Srmacklem		    /*
3783191783Srmacklem		     * Wait for outstanding I/O ops to be done.
3784191783Srmacklem		     */
3785191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
3786191783Srmacklem			if (igotlock) {
3787191783Srmacklem			    nfsv4_unlock(&clp->nfsc_lock, 0);
3788191783Srmacklem			    igotlock = 0;
3789191783Srmacklem			}
3790191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
3791191783Srmacklem			(void) nfsmsleep(&dp->nfsdl_rwlock,
3792191783Srmacklem			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
3793191783Srmacklem			continue;
3794191783Srmacklem		    }
3795191783Srmacklem		    needsrecall = 0;
3796191783Srmacklem		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3797191783Srmacklem			if (!LIST_EMPTY(&owp->nfsow_open)) {
3798191783Srmacklem			    needsrecall = 1;
3799191783Srmacklem			    break;
3800191783Srmacklem			}
3801191783Srmacklem		    }
3802191783Srmacklem		    if (!needsrecall) {
3803191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3804191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
3805191783Srmacklem				needsrecall = 1;
3806191783Srmacklem				break;
3807191783Srmacklem			    }
3808191783Srmacklem			}
3809191783Srmacklem		    }
3810191783Srmacklem		    if (needsrecall && !triedrecall) {
3811191783Srmacklem			islept = 0;
3812191783Srmacklem			while (!igotlock) {
3813191783Srmacklem			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
3814191783Srmacklem				&islept, NFSCLSTATEMUTEXPTR);
3815191783Srmacklem			    if (islept)
3816191783Srmacklem				break;
3817191783Srmacklem			}
3818191783Srmacklem			if (islept)
3819191783Srmacklem			    continue;
3820191783Srmacklem			NFSUNLOCKCLSTATE();
3821191783Srmacklem			cred = newnfs_getcred();
3822191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
3823191783Srmacklem			(void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p);
3824191783Srmacklem			NFSFREECRED(cred);
3825191783Srmacklem			triedrecall = 1;
3826191783Srmacklem			NFSLOCKCLSTATE();
3827191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
3828191783Srmacklem			igotlock = 0;
3829191783Srmacklem			continue;
3830191783Srmacklem		    }
3831191783Srmacklem		    *stp = dp->nfsdl_stateid;
3832191783Srmacklem		    retcnt = 1;
3833191783Srmacklem		    nfscl_cleandeleg(dp);
3834191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
3835191783Srmacklem		}
3836191783Srmacklem		if (igotlock)
3837191783Srmacklem		    nfsv4_unlock(&clp->nfsc_lock, 0);
3838191783Srmacklem		NFSUNLOCKCLSTATE();
3839191783Srmacklem		return (retcnt);
3840191783Srmacklem	}
3841191783Srmacklem}
3842191783Srmacklem
3843191783Srmacklem/*
3844191783Srmacklem * Look for associated delegation(s) that should be DelegReturned.
3845191783Srmacklem */
3846191783SrmacklemAPPLESTATIC int
3847191783Srmacklemnfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
3848191783Srmacklem    nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
3849191783Srmacklem{
3850191783Srmacklem	struct nfsclclient *clp;
3851191783Srmacklem	struct nfscldeleg *dp;
3852191783Srmacklem	struct nfsclowner *owp;
3853191783Srmacklem	struct nfscllockowner *lp;
3854191783Srmacklem	struct nfsmount *nmp;
3855191783Srmacklem	struct ucred *cred;
3856191783Srmacklem	struct nfsnode *np;
3857191783Srmacklem	int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
3858191783Srmacklem
3859191783Srmacklem	nmp = VFSTONFS(vnode_mount(fvp));
3860191783Srmacklem	*gotfdp = 0;
3861191783Srmacklem	*gottdp = 0;
3862191783Srmacklem	NFSLOCKCLSTATE();
3863191783Srmacklem	/*
3864191783Srmacklem	 * Loop around waiting for:
3865191783Srmacklem	 * - outstanding I/O operations on delegations to complete
3866191783Srmacklem	 * - for a delegation on fvp that has state, lock the client and
3867191783Srmacklem	 *   do a recall
3868191783Srmacklem	 * - return delegation(s) with no state.
3869191783Srmacklem	 */
3870191783Srmacklem	while (1) {
3871191783Srmacklem		clp = nfscl_findcl(nmp);
3872191783Srmacklem		if (clp == NULL) {
3873191783Srmacklem			NFSUNLOCKCLSTATE();
3874191783Srmacklem			return (retcnt);
3875191783Srmacklem		}
3876191783Srmacklem		np = VTONFS(fvp);
3877191783Srmacklem		dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
3878191783Srmacklem		    np->n_fhp->nfh_len);
3879191783Srmacklem		if (dp != NULL && *gotfdp == 0) {
3880191783Srmacklem		    /*
3881191783Srmacklem		     * Wait for outstanding I/O ops to be done.
3882191783Srmacklem		     */
3883191783Srmacklem		    if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
3884191783Srmacklem			if (igotlock) {
3885191783Srmacklem			    nfsv4_unlock(&clp->nfsc_lock, 0);
3886191783Srmacklem			    igotlock = 0;
3887191783Srmacklem			}
3888191783Srmacklem			dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
3889191783Srmacklem			(void) nfsmsleep(&dp->nfsdl_rwlock,
3890191783Srmacklem			    NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
3891191783Srmacklem			continue;
3892191783Srmacklem		    }
3893191783Srmacklem		    needsrecall = 0;
3894191783Srmacklem		    LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3895191783Srmacklem			if (!LIST_EMPTY(&owp->nfsow_open)) {
3896191783Srmacklem			    needsrecall = 1;
3897191783Srmacklem			    break;
3898191783Srmacklem			}
3899191783Srmacklem		    }
3900191783Srmacklem		    if (!needsrecall) {
3901191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3902191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
3903191783Srmacklem				needsrecall = 1;
3904191783Srmacklem				break;
3905191783Srmacklem			    }
3906191783Srmacklem			}
3907191783Srmacklem		    }
3908191783Srmacklem		    if (needsrecall && !triedrecall) {
3909191783Srmacklem			islept = 0;
3910191783Srmacklem			while (!igotlock) {
3911191783Srmacklem			    igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
3912191783Srmacklem				&islept, NFSCLSTATEMUTEXPTR);
3913191783Srmacklem			    if (islept)
3914191783Srmacklem				break;
3915191783Srmacklem			}
3916191783Srmacklem			if (islept)
3917191783Srmacklem			    continue;
3918191783Srmacklem			NFSUNLOCKCLSTATE();
3919191783Srmacklem			cred = newnfs_getcred();
3920191783Srmacklem			newnfs_copycred(&dp->nfsdl_cred, cred);
3921191783Srmacklem			(void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p);
3922191783Srmacklem			NFSFREECRED(cred);
3923191783Srmacklem			triedrecall = 1;
3924191783Srmacklem			NFSLOCKCLSTATE();
3925191783Srmacklem			nfsv4_unlock(&clp->nfsc_lock, 0);
3926191783Srmacklem			igotlock = 0;
3927191783Srmacklem			continue;
3928191783Srmacklem		    }
3929191783Srmacklem		    *fstp = dp->nfsdl_stateid;
3930191783Srmacklem		    retcnt++;
3931191783Srmacklem		    *gotfdp = 1;
3932191783Srmacklem		    nfscl_cleandeleg(dp);
3933191783Srmacklem		    nfscl_freedeleg(&clp->nfsc_deleg, dp);
3934191783Srmacklem		}
3935191783Srmacklem		if (igotlock) {
3936191783Srmacklem		    nfsv4_unlock(&clp->nfsc_lock, 0);
3937191783Srmacklem		    igotlock = 0;
3938191783Srmacklem		}
3939191783Srmacklem		if (tvp != NULL) {
3940191783Srmacklem		    np = VTONFS(tvp);
3941191783Srmacklem		    dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
3942191783Srmacklem			np->n_fhp->nfh_len);
3943191783Srmacklem		    if (dp != NULL && *gottdp == 0) {
3944191783Srmacklem			/*
3945191783Srmacklem			 * Wait for outstanding I/O ops to be done.
3946191783Srmacklem			 */
3947191783Srmacklem			if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
3948191783Srmacklem			    dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
3949191783Srmacklem			    (void) nfsmsleep(&dp->nfsdl_rwlock,
3950191783Srmacklem				NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
3951191783Srmacklem			    continue;
3952191783Srmacklem			}
3953191783Srmacklem			LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3954191783Srmacklem			    if (!LIST_EMPTY(&owp->nfsow_open)) {
3955191783Srmacklem				NFSUNLOCKCLSTATE();
3956191783Srmacklem				return (retcnt);
3957191783Srmacklem			    }
3958191783Srmacklem			}
3959191783Srmacklem			LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3960191783Srmacklem			    if (!LIST_EMPTY(&lp->nfsl_lock)) {
3961191783Srmacklem				NFSUNLOCKCLSTATE();
3962191783Srmacklem				return (retcnt);
3963191783Srmacklem			    }
3964191783Srmacklem			}
3965191783Srmacklem			*tstp = dp->nfsdl_stateid;
3966191783Srmacklem			retcnt++;
3967191783Srmacklem			*gottdp = 1;
3968191783Srmacklem			nfscl_cleandeleg(dp);
3969191783Srmacklem			nfscl_freedeleg(&clp->nfsc_deleg, dp);
3970191783Srmacklem		    }
3971191783Srmacklem		}
3972191783Srmacklem		NFSUNLOCKCLSTATE();
3973191783Srmacklem		return (retcnt);
3974191783Srmacklem	}
3975191783Srmacklem}
3976191783Srmacklem
3977191783Srmacklem/*
3978191783Srmacklem * Get a reference on the clientid associated with the mount point.
3979191783Srmacklem * Return 1 if success, 0 otherwise.
3980191783Srmacklem */
3981191783SrmacklemAPPLESTATIC int
3982191783Srmacklemnfscl_getref(struct nfsmount *nmp)
3983191783Srmacklem{
3984191783Srmacklem	struct nfsclclient *clp;
3985191783Srmacklem
3986191783Srmacklem	NFSLOCKCLSTATE();
3987191783Srmacklem	clp = nfscl_findcl(nmp);
3988191783Srmacklem	if (clp == NULL) {
3989191783Srmacklem		NFSUNLOCKCLSTATE();
3990191783Srmacklem		return (0);
3991191783Srmacklem	}
3992191783Srmacklem	nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR);
3993191783Srmacklem	NFSUNLOCKCLSTATE();
3994191783Srmacklem	return (1);
3995191783Srmacklem}
3996191783Srmacklem
3997191783Srmacklem/*
3998191783Srmacklem * Release a reference on a clientid acquired with the above call.
3999191783Srmacklem */
4000191783SrmacklemAPPLESTATIC void
4001191783Srmacklemnfscl_relref(struct nfsmount *nmp)
4002191783Srmacklem{
4003191783Srmacklem	struct nfsclclient *clp;
4004191783Srmacklem
4005191783Srmacklem	NFSLOCKCLSTATE();
4006191783Srmacklem	clp = nfscl_findcl(nmp);
4007191783Srmacklem	if (clp == NULL) {
4008191783Srmacklem		NFSUNLOCKCLSTATE();
4009191783Srmacklem		return;
4010191783Srmacklem	}
4011191783Srmacklem	nfsv4_relref(&clp->nfsc_lock);
4012191783Srmacklem	NFSUNLOCKCLSTATE();
4013191783Srmacklem}
4014191783Srmacklem
4015191783Srmacklem/*
4016191783Srmacklem * Save the size attribute in the delegation, since the nfsnode
4017191783Srmacklem * is going away.
4018191783Srmacklem */
4019191783SrmacklemAPPLESTATIC void
4020191783Srmacklemnfscl_reclaimnode(vnode_t vp)
4021191783Srmacklem{
4022191783Srmacklem	struct nfsclclient *clp;
4023191783Srmacklem	struct nfscldeleg *dp;
4024191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4025191783Srmacklem	struct nfsmount *nmp;
4026191783Srmacklem
4027191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4028191783Srmacklem	if (!NFSHASNFSV4(nmp))
4029191783Srmacklem		return;
4030191783Srmacklem	NFSLOCKCLSTATE();
4031191783Srmacklem	clp = nfscl_findcl(nmp);
4032191783Srmacklem	if (clp == NULL) {
4033191783Srmacklem		NFSUNLOCKCLSTATE();
4034191783Srmacklem		return;
4035191783Srmacklem	}
4036191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4037191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4038191783Srmacklem		dp->nfsdl_size = np->n_size;
4039191783Srmacklem	NFSUNLOCKCLSTATE();
4040191783Srmacklem}
4041191783Srmacklem
4042191783Srmacklem/*
4043191783Srmacklem * Get the saved size attribute in the delegation, since it is a
4044191783Srmacklem * newly allocated nfsnode.
4045191783Srmacklem */
4046191783SrmacklemAPPLESTATIC void
4047191783Srmacklemnfscl_newnode(vnode_t vp)
4048191783Srmacklem{
4049191783Srmacklem	struct nfsclclient *clp;
4050191783Srmacklem	struct nfscldeleg *dp;
4051191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4052191783Srmacklem	struct nfsmount *nmp;
4053191783Srmacklem
4054191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4055191783Srmacklem	if (!NFSHASNFSV4(nmp))
4056191783Srmacklem		return;
4057191783Srmacklem	NFSLOCKCLSTATE();
4058191783Srmacklem	clp = nfscl_findcl(nmp);
4059191783Srmacklem	if (clp == NULL) {
4060191783Srmacklem		NFSUNLOCKCLSTATE();
4061191783Srmacklem		return;
4062191783Srmacklem	}
4063191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4064191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4065191783Srmacklem		np->n_size = dp->nfsdl_size;
4066191783Srmacklem	NFSUNLOCKCLSTATE();
4067191783Srmacklem}
4068191783Srmacklem
4069191783Srmacklem/*
4070191783Srmacklem * If there is a valid write delegation for this file, set the modtime
4071191783Srmacklem * to the local clock time.
4072191783Srmacklem */
4073191783SrmacklemAPPLESTATIC void
4074191783Srmacklemnfscl_delegmodtime(vnode_t vp)
4075191783Srmacklem{
4076191783Srmacklem	struct nfsclclient *clp;
4077191783Srmacklem	struct nfscldeleg *dp;
4078191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4079191783Srmacklem	struct nfsmount *nmp;
4080191783Srmacklem
4081191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4082191783Srmacklem	if (!NFSHASNFSV4(nmp))
4083191783Srmacklem		return;
4084191783Srmacklem	NFSLOCKCLSTATE();
4085191783Srmacklem	clp = nfscl_findcl(nmp);
4086191783Srmacklem	if (clp == NULL) {
4087191783Srmacklem		NFSUNLOCKCLSTATE();
4088191783Srmacklem		return;
4089191783Srmacklem	}
4090191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4091191783Srmacklem	if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
4092191783Srmacklem		NFSGETNANOTIME(&dp->nfsdl_modtime);
4093191783Srmacklem		dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
4094191783Srmacklem	}
4095191783Srmacklem	NFSUNLOCKCLSTATE();
4096191783Srmacklem}
4097191783Srmacklem
4098191783Srmacklem/*
4099191783Srmacklem * If there is a valid write delegation for this file with a modtime set,
4100191783Srmacklem * put that modtime in mtime.
4101191783Srmacklem */
4102191783SrmacklemAPPLESTATIC void
4103191783Srmacklemnfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
4104191783Srmacklem{
4105191783Srmacklem	struct nfsclclient *clp;
4106191783Srmacklem	struct nfscldeleg *dp;
4107191783Srmacklem	struct nfsnode *np = VTONFS(vp);
4108191783Srmacklem	struct nfsmount *nmp;
4109191783Srmacklem
4110191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4111191783Srmacklem	if (!NFSHASNFSV4(nmp))
4112191783Srmacklem		return;
4113191783Srmacklem	NFSLOCKCLSTATE();
4114191783Srmacklem	clp = nfscl_findcl(nmp);
4115191783Srmacklem	if (clp == NULL) {
4116191783Srmacklem		NFSUNLOCKCLSTATE();
4117191783Srmacklem		return;
4118191783Srmacklem	}
4119191783Srmacklem	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4120191783Srmacklem	if (dp != NULL &&
4121191783Srmacklem	    (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
4122191783Srmacklem	    (NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
4123191783Srmacklem		*mtime = dp->nfsdl_modtime;
4124191783Srmacklem	NFSUNLOCKCLSTATE();
4125191783Srmacklem}
4126191783Srmacklem
4127191783Srmacklemstatic int
4128191783Srmacklemnfscl_errmap(struct nfsrv_descript *nd)
4129191783Srmacklem{
4130191783Srmacklem	short *defaulterrp, *errp;
4131191783Srmacklem
4132191783Srmacklem	if (!nd->nd_repstat)
4133191783Srmacklem		return (0);
4134191783Srmacklem	if (nd->nd_procnum == NFSPROC_NOOP)
4135191783Srmacklem		return (txdr_unsigned(nd->nd_repstat & 0xffff));
4136191783Srmacklem	if (nd->nd_repstat == EBADRPC)
4137191783Srmacklem		return (txdr_unsigned(NFSERR_BADXDR));
4138191783Srmacklem	if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
4139191783Srmacklem	    nd->nd_repstat == NFSERR_OPILLEGAL)
4140191783Srmacklem		return (txdr_unsigned(nd->nd_repstat));
4141191783Srmacklem	errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
4142191783Srmacklem	while (*++errp)
4143191783Srmacklem		if (*errp == (short)nd->nd_repstat)
4144191783Srmacklem			return (txdr_unsigned(nd->nd_repstat));
4145191783Srmacklem	return (txdr_unsigned(*defaulterrp));
4146191783Srmacklem}
4147191783Srmacklem
4148