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