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