source: rtems-libbsd/freebsd/sys/fs/nfsclient/nfs_clstate.c @ 6e4709b

6-freebsd-12
Last change on this file since 6e4709b was 6e4709b, checked in by Sebastian Huber <sebastian.huber@…>, on 01/05/23 at 16:42:48

vfs/nfs: Revert white space changes

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