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