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