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