nfs_clstate.c revision 201439
1191783Srmacklem/*- 2191783Srmacklem * Copyright (c) 2009 Rick Macklem, University of Guelph 3191783Srmacklem * All rights reserved. 4191783Srmacklem * 5191783Srmacklem * Redistribution and use in source and binary forms, with or without 6191783Srmacklem * modification, are permitted provided that the following conditions 7191783Srmacklem * are met: 8191783Srmacklem * 1. Redistributions of source code must retain the above copyright 9191783Srmacklem * notice, this list of conditions and the following disclaimer. 10191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright 11191783Srmacklem * notice, this list of conditions and the following disclaimer in the 12191783Srmacklem * documentation and/or other materials provided with the distribution. 13191783Srmacklem * 14191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17191783Srmacklem * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24191783Srmacklem * SUCH DAMAGE. 25191783Srmacklem * 26191783Srmacklem */ 27191783Srmacklem 28191783Srmacklem#include <sys/cdefs.h> 29191783Srmacklem__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clstate.c 201439 2010-01-03 18:27:10Z rmacklem $"); 30191783Srmacklem 31191783Srmacklem/* 32191783Srmacklem * These functions implement the client side state handling for NFSv4. 33191783Srmacklem * NFSv4 state handling: 34191783Srmacklem * - A lockowner is used to determine lock contention, so it 35191783Srmacklem * corresponds directly to a Posix pid. (1 to 1 mapping) 36191783Srmacklem * - The correct granularity of an OpenOwner is not nearly so 37191783Srmacklem * obvious. An OpenOwner does the following: 38191783Srmacklem * - provides a serial sequencing of Open/Close/Lock-with-new-lockowner 39198289Sjh * - is used to check for Open/Share contention (not applicable to 40191783Srmacklem * this client, since all Opens are Deny_None) 41198289Sjh * As such, I considered both extreme. 42191783Srmacklem * 1 OpenOwner per ClientID - Simple to manage, but fully serializes 43191783Srmacklem * all Open, Close and Lock (with a new lockowner) Ops. 44191783Srmacklem * 1 OpenOwner for each Open - This one results in an OpenConfirm for 45191783Srmacklem * every Open, for most servers. 46191783Srmacklem * So, I chose to use the same mapping as I did for LockOwnwers. 47191783Srmacklem * The main concern here is that you can end up with multiple Opens 48191783Srmacklem * for the same File Handle, but on different OpenOwners (opens 49191783Srmacklem * inherited from parents, grandparents...) and you do not know 50191783Srmacklem * which of these the vnodeop close applies to. This is handled by 51191783Srmacklem * delaying the Close Op(s) until all of the Opens have been closed. 52191783Srmacklem * (It is not yet obvious if this is the correct granularity.) 53198289Sjh * - How the code handles serialization: 54198289Sjh * - For the ClientId, it uses an exclusive lock while getting its 55191783Srmacklem * SetClientId and during recovery. Otherwise, it uses a shared 56191783Srmacklem * lock via a reference count. 57191783Srmacklem * - For the rest of the data structures, it uses an SMP mutex 58191783Srmacklem * (once the nfs client is SMP safe) and doesn't sleep while 59191783Srmacklem * manipulating the linked lists. 60191783Srmacklem * - The serialization of Open/Close/Lock/LockU falls out in the 61191783Srmacklem * "wash", since OpenOwners and LockOwners are both mapped from 62191783Srmacklem * Posix pid. In other words, there is only one Posix pid using 63191783Srmacklem * any given owner, so that owner is serialized. (If you change 64191783Srmacklem * the granularity of the OpenOwner, then code must be added to 65191783Srmacklem * serialize Ops on the OpenOwner.) 66191783Srmacklem * - When to get rid of OpenOwners and LockOwners. 67191783Srmacklem * - When a process exits, it calls nfscl_cleanup(), which goes 68191783Srmacklem * through the client list looking for all Open and Lock Owners. 69191783Srmacklem * When one is found, it is marked "defunct" or in the case of 70191783Srmacklem * an OpenOwner without any Opens, freed. 71191783Srmacklem * The renew thread scans for defunct Owners and gets rid of them, 72191783Srmacklem * if it can. The LockOwners will also be deleted when the 73191783Srmacklem * associated Open is closed. 74191783Srmacklem * - If the LockU or Close Op(s) fail during close in a way 75191783Srmacklem * that could be recovered upon retry, they are relinked to the 76191783Srmacklem * ClientId's defunct open list and retried by the renew thread 77191783Srmacklem * until they succeed or an unmount/recovery occurs. 78191783Srmacklem * (Since we are done with them, they do not need to be recovered.) 79191783Srmacklem */ 80191783Srmacklem 81191783Srmacklem#ifndef APPLEKEXT 82191783Srmacklem#include <fs/nfs/nfsport.h> 83191783Srmacklem 84191783Srmacklem/* 85191783Srmacklem * Global variables 86191783Srmacklem */ 87191783Srmacklemextern struct nfsstats newnfsstats; 88191783Srmacklemextern struct nfsreqhead nfsd_reqq; 89191783SrmacklemNFSREQSPINLOCK; 90191783SrmacklemNFSCLSTATEMUTEX; 91191783Srmacklemint nfscl_inited = 0; 92191783Srmacklemstruct nfsclhead nfsclhead; /* Head of clientid list */ 93191783Srmacklemint nfscl_deleghighwater = NFSCLDELEGHIGHWATER; 94191783Srmacklem#endif /* !APPLEKEXT */ 95191783Srmacklem 96191783Srmacklemstatic int nfscl_delegcnt = 0; 97191783Srmacklemstatic int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *, 98191783Srmacklem NFSPROC_T *, u_int32_t, struct nfsclowner **, struct nfsclopen **); 99191783Srmacklemstatic void nfscl_clrelease(struct nfsclclient *); 100191783Srmacklemstatic void nfscl_cleanclient(struct nfsclclient *); 101191783Srmacklemstatic void nfscl_expireclient(struct nfsclclient *, struct nfsmount *, 102191783Srmacklem struct ucred *, NFSPROC_T *); 103191783Srmacklemstatic int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *, 104191783Srmacklem struct nfsmount *, struct ucred *, NFSPROC_T *); 105191783Srmacklemstatic void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *); 106191783Srmacklemstatic void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *, 107191783Srmacklem struct nfscllock *, int); 108191783Srmacklemstatic int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **, 109191783Srmacklem struct nfscllock **, int); 110191783Srmacklemstatic void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *); 111191783Srmacklemstatic u_int32_t nfscl_nextcbident(void); 112191783Srmacklemstatic mount_t nfscl_getmnt(u_int32_t); 113191783Srmacklemstatic struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *, 114191783Srmacklem int); 115191783Srmacklemstatic int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *, 116191783Srmacklem u_int8_t *, struct nfscllock **); 117191783Srmacklemstatic void nfscl_freelockowner(struct nfscllockowner *, int); 118191783Srmacklemstatic void nfscl_freealllocks(struct nfscllockownerhead *, int); 119201439Srmacklemstatic int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int, 120201439Srmacklem struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **); 121191783Srmacklemstatic void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *, 122191783Srmacklem struct nfsclowner **, struct nfsclowner **, struct nfsclopen **, 123191783Srmacklem struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *); 124191783Srmacklemstatic int nfscl_moveopen(vnode_t , struct nfsclclient *, 125191783Srmacklem struct nfsmount *, struct nfsclopen *, struct nfsclowner *, 126191783Srmacklem struct nfscldeleg *, struct ucred *, NFSPROC_T *); 127191783Srmacklemstatic void nfscl_totalrecall(struct nfsclclient *); 128191783Srmacklemstatic int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *, 129191783Srmacklem struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *); 130191783Srmacklemstatic int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int, 131191783Srmacklem u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int, 132191783Srmacklem struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *); 133191783Srmacklemstatic int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *, 134191783Srmacklem int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short, 135191783Srmacklem struct ucred *, NFSPROC_T *); 136191783Srmacklemstatic int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t, 137191783Srmacklem struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *); 138191783Srmacklemstatic void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *); 139191783Srmacklemstatic int nfscl_errmap(struct nfsrv_descript *); 140191783Srmacklemstatic void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *); 141191783Srmacklemstatic int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *, 142191783Srmacklem struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *); 143191783Srmacklemstatic void nfscl_freeopenowner(struct nfsclowner *, int); 144191783Srmacklemstatic void nfscl_cleandeleg(struct nfscldeleg *); 145191783Srmacklemstatic int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *, 146191783Srmacklem struct nfsmount *, NFSPROC_T *); 147191783Srmacklem 148191783Srmacklemstatic short nfscberr_null[] = { 149191783Srmacklem 0, 150191783Srmacklem 0, 151191783Srmacklem}; 152191783Srmacklem 153191783Srmacklemstatic short nfscberr_getattr[] = { 154191783Srmacklem NFSERR_RESOURCE, 155191783Srmacklem NFSERR_BADHANDLE, 156191783Srmacklem NFSERR_BADXDR, 157191783Srmacklem NFSERR_RESOURCE, 158191783Srmacklem NFSERR_SERVERFAULT, 159191783Srmacklem 0, 160191783Srmacklem}; 161191783Srmacklem 162191783Srmacklemstatic short nfscberr_recall[] = { 163191783Srmacklem NFSERR_RESOURCE, 164191783Srmacklem NFSERR_BADHANDLE, 165191783Srmacklem NFSERR_BADSTATEID, 166191783Srmacklem NFSERR_BADXDR, 167191783Srmacklem NFSERR_RESOURCE, 168191783Srmacklem NFSERR_SERVERFAULT, 169191783Srmacklem 0, 170191783Srmacklem}; 171191783Srmacklem 172191783Srmacklemstatic short *nfscl_cberrmap[] = { 173191783Srmacklem nfscberr_null, 174191783Srmacklem nfscberr_null, 175191783Srmacklem nfscberr_null, 176191783Srmacklem nfscberr_getattr, 177191783Srmacklem nfscberr_recall 178191783Srmacklem}; 179191783Srmacklem 180191783Srmacklem#define NETFAMILY(clp) \ 181191783Srmacklem (((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET) 182191783Srmacklem 183191783Srmacklem/* 184191783Srmacklem * Called for an open operation. 185191783Srmacklem * If the nfhp argument is NULL, just get an openowner. 186191783Srmacklem */ 187191783SrmacklemAPPLESTATIC int 188191783Srmacklemnfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg, 189191783Srmacklem struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp, 190191783Srmacklem struct nfsclopen **opp, int *newonep, int *retp, int lockit) 191191783Srmacklem{ 192191783Srmacklem struct nfsclclient *clp; 193191783Srmacklem struct nfsclowner *owp, *nowp; 194191783Srmacklem struct nfsclopen *op = NULL, *nop = NULL; 195191783Srmacklem struct nfscldeleg *dp; 196191783Srmacklem struct nfsclownerhead *ohp; 197191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 198191783Srmacklem int ret; 199191783Srmacklem 200191783Srmacklem if (newonep != NULL) 201191783Srmacklem *newonep = 0; 202191783Srmacklem if (opp != NULL) 203191783Srmacklem *opp = NULL; 204191783Srmacklem if (owpp != NULL) 205191783Srmacklem *owpp = NULL; 206191783Srmacklem 207191783Srmacklem /* 208191783Srmacklem * Might need one or both of these, so MALLOC them now, to 209191783Srmacklem * avoid a tsleep() in MALLOC later. 210191783Srmacklem */ 211191783Srmacklem MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner), 212191783Srmacklem M_NFSCLOWNER, M_WAITOK); 213191783Srmacklem if (nfhp != NULL) 214191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 215191783Srmacklem fhlen - 1, M_NFSCLOPEN, M_WAITOK); 216191783Srmacklem ret = nfscl_getcl(vp, cred, p, &clp); 217191783Srmacklem if (ret != 0) { 218191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 219191783Srmacklem if (nop != NULL) 220191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 221191783Srmacklem return (ret); 222191783Srmacklem } 223191783Srmacklem 224191783Srmacklem /* 225191783Srmacklem * Get the Open iff it already exists. 226191783Srmacklem * If none found, add the new one or return error, depending upon 227191783Srmacklem * "create". 228191783Srmacklem */ 229191783Srmacklem nfscl_filllockowner(p, own); 230191783Srmacklem NFSLOCKCLSTATE(); 231191783Srmacklem dp = NULL; 232191783Srmacklem /* First check the delegation list */ 233191783Srmacklem if (nfhp != NULL && usedeleg) { 234191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) { 235191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 236191783Srmacklem !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) { 237191783Srmacklem if (!(amode & NFSV4OPEN_ACCESSWRITE) || 238191783Srmacklem (dp->nfsdl_flags & NFSCLDL_WRITE)) 239191783Srmacklem break; 240191783Srmacklem dp = NULL; 241191783Srmacklem break; 242191783Srmacklem } 243191783Srmacklem } 244191783Srmacklem } 245191783Srmacklem 246191783Srmacklem if (dp != NULL) 247191783Srmacklem ohp = &dp->nfsdl_owner; 248191783Srmacklem else 249191783Srmacklem ohp = &clp->nfsc_owner; 250191783Srmacklem /* Now, search for an openowner */ 251191783Srmacklem LIST_FOREACH(owp, ohp, nfsow_list) { 252191783Srmacklem if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN)) 253191783Srmacklem break; 254191783Srmacklem } 255191783Srmacklem 256191783Srmacklem /* 257191783Srmacklem * Create a new open, as required. 258191783Srmacklem */ 259191783Srmacklem nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen, 260191783Srmacklem newonep); 261191783Srmacklem 262191783Srmacklem /* 263191783Srmacklem * Serialize modifications to the open owner for multiple threads 264191783Srmacklem * within the same process using a read/write sleep lock. 265191783Srmacklem */ 266191783Srmacklem if (lockit) 267191783Srmacklem nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR); 268191783Srmacklem NFSUNLOCKCLSTATE(); 269191783Srmacklem if (nowp != NULL) 270191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 271191783Srmacklem if (nop != NULL) 272191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 273191783Srmacklem if (owpp != NULL) 274191783Srmacklem *owpp = owp; 275191783Srmacklem if (opp != NULL) 276191783Srmacklem *opp = op; 277191783Srmacklem if (retp != NULL) 278191783Srmacklem *retp = NFSCLOPEN_OK; 279191783Srmacklem 280191783Srmacklem /* 281191783Srmacklem * Now, check the mode on the open and return the appropriate 282191783Srmacklem * value. 283191783Srmacklem */ 284191783Srmacklem if (op != NULL && (amode & ~(op->nfso_mode))) { 285191783Srmacklem op->nfso_mode |= amode; 286191783Srmacklem if (retp != NULL && dp == NULL) 287191783Srmacklem *retp = NFSCLOPEN_DOOPEN; 288191783Srmacklem } 289191783Srmacklem return (0); 290191783Srmacklem} 291191783Srmacklem 292191783Srmacklem/* 293191783Srmacklem * Create a new open, as required. 294191783Srmacklem */ 295191783Srmacklemstatic void 296191783Srmacklemnfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp, 297191783Srmacklem struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp, 298191783Srmacklem struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen, 299191783Srmacklem int *newonep) 300191783Srmacklem{ 301191783Srmacklem struct nfsclowner *owp = *owpp, *nowp; 302191783Srmacklem struct nfsclopen *op, *nop; 303191783Srmacklem 304191783Srmacklem if (nowpp != NULL) 305191783Srmacklem nowp = *nowpp; 306191783Srmacklem else 307191783Srmacklem nowp = NULL; 308191783Srmacklem if (nopp != NULL) 309191783Srmacklem nop = *nopp; 310191783Srmacklem else 311191783Srmacklem nop = NULL; 312191783Srmacklem if (owp == NULL && nowp != NULL) { 313191783Srmacklem NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN); 314191783Srmacklem LIST_INIT(&nowp->nfsow_open); 315191783Srmacklem nowp->nfsow_clp = clp; 316191783Srmacklem nowp->nfsow_seqid = 0; 317191783Srmacklem nowp->nfsow_defunct = 0; 318191783Srmacklem nfscl_lockinit(&nowp->nfsow_rwlock); 319191783Srmacklem if (dp != NULL) { 320191783Srmacklem newnfsstats.cllocalopenowners++; 321191783Srmacklem LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list); 322191783Srmacklem } else { 323191783Srmacklem newnfsstats.clopenowners++; 324191783Srmacklem LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list); 325191783Srmacklem } 326191783Srmacklem owp = *owpp = nowp; 327191783Srmacklem *nowpp = NULL; 328191783Srmacklem if (newonep != NULL) 329191783Srmacklem *newonep = 1; 330191783Srmacklem } 331191783Srmacklem 332191783Srmacklem /* If an fhp has been specified, create an Open as well. */ 333191783Srmacklem if (fhp != NULL) { 334191783Srmacklem /* and look for the correct open, based upon FH */ 335191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 336191783Srmacklem if (op->nfso_fhlen == fhlen && 337191783Srmacklem !NFSBCMP(op->nfso_fh, fhp, fhlen)) 338191783Srmacklem break; 339191783Srmacklem } 340191783Srmacklem if (op == NULL && nop != NULL) { 341191783Srmacklem nop->nfso_own = owp; 342191783Srmacklem nop->nfso_mode = 0; 343191783Srmacklem nop->nfso_opencnt = 0; 344191783Srmacklem nop->nfso_posixlock = 1; 345191783Srmacklem nop->nfso_fhlen = fhlen; 346191783Srmacklem NFSBCOPY(fhp, nop->nfso_fh, fhlen); 347191783Srmacklem LIST_INIT(&nop->nfso_lock); 348191783Srmacklem nop->nfso_stateid.seqid = 0; 349191783Srmacklem nop->nfso_stateid.other[0] = 0; 350191783Srmacklem nop->nfso_stateid.other[1] = 0; 351191783Srmacklem nop->nfso_stateid.other[2] = 0; 352191783Srmacklem if (dp != NULL) { 353191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 354191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, 355191783Srmacklem nfsdl_list); 356191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 357191783Srmacklem newnfsstats.cllocalopens++; 358191783Srmacklem } else { 359191783Srmacklem newnfsstats.clopens++; 360191783Srmacklem } 361191783Srmacklem LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list); 362191783Srmacklem *opp = nop; 363191783Srmacklem *nopp = NULL; 364191783Srmacklem if (newonep != NULL) 365191783Srmacklem *newonep = 1; 366191783Srmacklem } else { 367191783Srmacklem *opp = op; 368191783Srmacklem } 369191783Srmacklem } 370191783Srmacklem} 371191783Srmacklem 372191783Srmacklem/* 373191783Srmacklem * Called to find/add a delegation to a client. 374191783Srmacklem */ 375191783SrmacklemAPPLESTATIC int 376191783Srmacklemnfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp, 377191783Srmacklem int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp) 378191783Srmacklem{ 379191783Srmacklem struct nfscldeleg *dp = *dpp, *tdp; 380191783Srmacklem 381191783Srmacklem /* 382191783Srmacklem * First, if we have received a Read delegation for a file on a 383191783Srmacklem * read/write file system, just return it, because they aren't 384191783Srmacklem * useful, imho. 385191783Srmacklem */ 386191783Srmacklem if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) && 387191783Srmacklem (dp->nfsdl_flags & NFSCLDL_READ)) { 388191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p); 389191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 390191783Srmacklem *dpp = NULL; 391191783Srmacklem return (0); 392191783Srmacklem } 393191783Srmacklem 394191783Srmacklem /* Look for the correct deleg, based upon FH */ 395191783Srmacklem NFSLOCKCLSTATE(); 396191783Srmacklem tdp = nfscl_finddeleg(clp, nfhp, fhlen); 397191783Srmacklem if (tdp == NULL) { 398191783Srmacklem if (dp == NULL) { 399191783Srmacklem NFSUNLOCKCLSTATE(); 400191783Srmacklem return (NFSERR_BADSTATEID); 401191783Srmacklem } 402191783Srmacklem *dpp = NULL; 403191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list); 404191783Srmacklem LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp, 405191783Srmacklem nfsdl_hash); 406191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 407191783Srmacklem newnfsstats.cldelegates++; 408191783Srmacklem nfscl_delegcnt++; 409191783Srmacklem } else { 410191783Srmacklem /* 411191783Srmacklem * Delegation already exists, what do we do if a new one?? 412191783Srmacklem */ 413191783Srmacklem if (dp != NULL) { 414191783Srmacklem printf("Deleg already exists!\n"); 415191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 416191783Srmacklem *dpp = NULL; 417191783Srmacklem } else { 418191783Srmacklem *dpp = tdp; 419191783Srmacklem } 420191783Srmacklem } 421191783Srmacklem NFSUNLOCKCLSTATE(); 422191783Srmacklem return (0); 423191783Srmacklem} 424191783Srmacklem 425191783Srmacklem/* 426191783Srmacklem * Find a delegation for this file handle. Return NULL upon failure. 427191783Srmacklem */ 428191783Srmacklemstatic struct nfscldeleg * 429191783Srmacklemnfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen) 430191783Srmacklem{ 431191783Srmacklem struct nfscldeleg *dp; 432191783Srmacklem 433191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) { 434191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 435191783Srmacklem !NFSBCMP(dp->nfsdl_fh, fhp, fhlen)) 436191783Srmacklem break; 437191783Srmacklem } 438191783Srmacklem return (dp); 439191783Srmacklem} 440191783Srmacklem 441191783Srmacklem/* 442191783Srmacklem * Get a stateid for an I/O operation. First, look for an open and iff 443191783Srmacklem * found, return either a lockowner stateid or the open stateid. 444191783Srmacklem * If no Open is found, just return error and the special stateid of all zeros. 445191783Srmacklem */ 446191783SrmacklemAPPLESTATIC int 447191783Srmacklemnfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode, 448191783Srmacklem struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp, 449191783Srmacklem void **lckpp) 450191783Srmacklem{ 451191783Srmacklem struct nfsclclient *clp; 452191783Srmacklem struct nfsclowner *owp; 453195510Srmacklem struct nfsclopen *op = NULL; 454191783Srmacklem struct nfscllockowner *lp; 455191783Srmacklem struct nfscldeleg *dp; 456191783Srmacklem struct nfsnode *np; 457191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 458191783Srmacklem int error, done; 459191783Srmacklem 460191783Srmacklem *lckpp = NULL; 461191783Srmacklem /* 462191783Srmacklem * Initially, just set the special stateid of all zeros. 463191783Srmacklem */ 464191783Srmacklem stateidp->seqid = 0; 465191783Srmacklem stateidp->other[0] = 0; 466191783Srmacklem stateidp->other[1] = 0; 467191783Srmacklem stateidp->other[2] = 0; 468191783Srmacklem if (vnode_vtype(vp) != VREG) 469191783Srmacklem return (EISDIR); 470191783Srmacklem np = VTONFS(vp); 471191783Srmacklem NFSLOCKCLSTATE(); 472191783Srmacklem clp = nfscl_findcl(VFSTONFS(vnode_mount(vp))); 473191783Srmacklem if (clp == NULL) { 474191783Srmacklem NFSUNLOCKCLSTATE(); 475191783Srmacklem return (EACCES); 476191783Srmacklem } 477191783Srmacklem 478191783Srmacklem /* 479191783Srmacklem * First, look for a delegation. 480191783Srmacklem */ 481191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) { 482191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 483191783Srmacklem !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) { 484191783Srmacklem if (!(mode & NFSV4OPEN_ACCESSWRITE) || 485191783Srmacklem (dp->nfsdl_flags & NFSCLDL_WRITE)) { 486191783Srmacklem stateidp->seqid = dp->nfsdl_stateid.seqid; 487191783Srmacklem stateidp->other[0] = dp->nfsdl_stateid.other[0]; 488191783Srmacklem stateidp->other[1] = dp->nfsdl_stateid.other[1]; 489191783Srmacklem stateidp->other[2] = dp->nfsdl_stateid.other[2]; 490191783Srmacklem if (!(np->n_flag & NDELEGRECALL)) { 491191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, 492191783Srmacklem nfsdl_list); 493191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, 494191783Srmacklem nfsdl_list); 495191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 496191783Srmacklem 120; 497191783Srmacklem dp->nfsdl_rwlock.nfslock_usecnt++; 498191783Srmacklem *lckpp = (void *)&dp->nfsdl_rwlock; 499191783Srmacklem } 500191783Srmacklem NFSUNLOCKCLSTATE(); 501191783Srmacklem return (0); 502191783Srmacklem } 503191783Srmacklem break; 504191783Srmacklem } 505191783Srmacklem } 506191783Srmacklem 507191783Srmacklem if (p != NULL) { 508191783Srmacklem /* 509191783Srmacklem * If p != NULL, we want to search the parentage tree 510191783Srmacklem * for a matching OpenOwner and use that. 511191783Srmacklem */ 512191783Srmacklem nfscl_filllockowner(p, own); 513191783Srmacklem error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, NULL, p, 514191783Srmacklem mode, NULL, &op); 515195510Srmacklem if (error == 0) { 516195510Srmacklem /* now look for a lockowner */ 517195510Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 518195510Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 519195510Srmacklem NFSV4CL_LOCKNAMELEN)) { 520195510Srmacklem stateidp->seqid = 521195510Srmacklem lp->nfsl_stateid.seqid; 522195510Srmacklem stateidp->other[0] = 523195510Srmacklem lp->nfsl_stateid.other[0]; 524195510Srmacklem stateidp->other[1] = 525195510Srmacklem lp->nfsl_stateid.other[1]; 526195510Srmacklem stateidp->other[2] = 527195510Srmacklem lp->nfsl_stateid.other[2]; 528195510Srmacklem NFSUNLOCKCLSTATE(); 529195510Srmacklem return (0); 530195510Srmacklem } 531195510Srmacklem } 532191783Srmacklem } 533195510Srmacklem } 534195510Srmacklem if (op == NULL) { 535195510Srmacklem /* If not found, just look for any OpenOwner that will work. */ 536191783Srmacklem done = 0; 537191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 538191783Srmacklem while (!done && owp != NULL) { 539195510Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 540195510Srmacklem if (op->nfso_fhlen == fhlen && 541195510Srmacklem !NFSBCMP(op->nfso_fh, nfhp, fhlen) && 542195510Srmacklem (mode & op->nfso_mode) == mode) { 543195510Srmacklem done = 1; 544195510Srmacklem break; 545195510Srmacklem } 546191783Srmacklem } 547195510Srmacklem if (!done) 548195510Srmacklem owp = LIST_NEXT(owp, nfsow_list); 549191783Srmacklem } 550191783Srmacklem if (!done) { 551191783Srmacklem NFSUNLOCKCLSTATE(); 552191783Srmacklem return (ENOENT); 553191783Srmacklem } 554191783Srmacklem /* for read aheads or write behinds, use the open cred */ 555191783Srmacklem newnfs_copycred(&op->nfso_cred, cred); 556191783Srmacklem } 557191783Srmacklem 558191783Srmacklem /* 559191783Srmacklem * No lock stateid, so return the open stateid. 560191783Srmacklem */ 561191783Srmacklem stateidp->seqid = op->nfso_stateid.seqid; 562191783Srmacklem stateidp->other[0] = op->nfso_stateid.other[0]; 563191783Srmacklem stateidp->other[1] = op->nfso_stateid.other[1]; 564191783Srmacklem stateidp->other[2] = op->nfso_stateid.other[2]; 565191783Srmacklem NFSUNLOCKCLSTATE(); 566191783Srmacklem return (0); 567191783Srmacklem} 568191783Srmacklem 569191783Srmacklem/* 570191783Srmacklem * Get an existing open. Search up the parentage tree for a match and 571191783Srmacklem * return with the first one found. 572191783Srmacklem */ 573191783Srmacklemstatic int 574191783Srmacklemnfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen, 575191783Srmacklem u_int8_t *rown, NFSPROC_T *p, u_int32_t mode, struct nfsclowner **owpp, 576191783Srmacklem struct nfsclopen **opp) 577191783Srmacklem{ 578191783Srmacklem struct nfsclowner *owp = NULL; 579191783Srmacklem struct nfsclopen *op; 580191783Srmacklem NFSPROC_T *nproc; 581191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp; 582191783Srmacklem 583191783Srmacklem nproc = p; 584191783Srmacklem op = NULL; 585191783Srmacklem while (op == NULL && (nproc != NULL || rown != NULL)) { 586191783Srmacklem if (nproc != NULL) { 587191783Srmacklem nfscl_filllockowner(nproc, own); 588191783Srmacklem ownp = own; 589191783Srmacklem } else { 590191783Srmacklem ownp = rown; 591191783Srmacklem } 592191783Srmacklem /* Search the client list */ 593191783Srmacklem LIST_FOREACH(owp, ohp, nfsow_list) { 594191783Srmacklem if (!NFSBCMP(owp->nfsow_owner, ownp, 595191783Srmacklem NFSV4CL_LOCKNAMELEN)) 596191783Srmacklem break; 597191783Srmacklem } 598191783Srmacklem if (owp != NULL) { 599191783Srmacklem /* and look for the correct open */ 600191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 601191783Srmacklem if (op->nfso_fhlen == fhlen && 602191783Srmacklem !NFSBCMP(op->nfso_fh, nfhp, fhlen) 603191783Srmacklem && (op->nfso_mode & mode) == mode) { 604191783Srmacklem break; 605191783Srmacklem } 606191783Srmacklem } 607191783Srmacklem } 608191783Srmacklem if (rown != NULL) 609191783Srmacklem break; 610191783Srmacklem if (op == NULL) 611191783Srmacklem nproc = nfscl_getparent(nproc); 612191783Srmacklem } 613191783Srmacklem if (op == NULL) { 614191783Srmacklem return (EBADF); 615191783Srmacklem } 616191783Srmacklem if (owpp) 617191783Srmacklem *owpp = owp; 618191783Srmacklem *opp = op; 619191783Srmacklem return (0); 620191783Srmacklem} 621191783Srmacklem 622191783Srmacklem/* 623191783Srmacklem * Release use of an open owner. Called when open operations are done 624191783Srmacklem * with the open owner. 625191783Srmacklem */ 626191783SrmacklemAPPLESTATIC void 627191783Srmacklemnfscl_ownerrelease(struct nfsclowner *owp, __unused int error, 628191783Srmacklem __unused int candelete, int unlocked) 629191783Srmacklem{ 630191783Srmacklem 631191783Srmacklem if (owp == NULL) 632191783Srmacklem return; 633191783Srmacklem NFSLOCKCLSTATE(); 634191783Srmacklem if (!unlocked) 635191783Srmacklem nfscl_lockunlock(&owp->nfsow_rwlock); 636191783Srmacklem nfscl_clrelease(owp->nfsow_clp); 637191783Srmacklem NFSUNLOCKCLSTATE(); 638191783Srmacklem} 639191783Srmacklem 640191783Srmacklem/* 641191783Srmacklem * Release use of an open structure under an open owner. 642191783Srmacklem */ 643191783SrmacklemAPPLESTATIC void 644191783Srmacklemnfscl_openrelease(struct nfsclopen *op, int error, int candelete) 645191783Srmacklem{ 646191783Srmacklem struct nfsclclient *clp; 647191783Srmacklem struct nfsclowner *owp; 648191783Srmacklem 649191783Srmacklem if (op == NULL) 650191783Srmacklem return; 651191783Srmacklem NFSLOCKCLSTATE(); 652191783Srmacklem owp = op->nfso_own; 653191783Srmacklem nfscl_lockunlock(&owp->nfsow_rwlock); 654191783Srmacklem clp = owp->nfsow_clp; 655191783Srmacklem if (error && candelete && op->nfso_opencnt == 0) 656191783Srmacklem nfscl_freeopen(op, 0); 657191783Srmacklem nfscl_clrelease(clp); 658191783Srmacklem NFSUNLOCKCLSTATE(); 659191783Srmacklem} 660191783Srmacklem 661191783Srmacklem/* 662191783Srmacklem * Called to get a clientid structure. It will optionally lock the 663191783Srmacklem * client data structures to do the SetClientId/SetClientId_confirm, 664191783Srmacklem * but will release that lock and return the clientid with a refernce 665191783Srmacklem * count on it. 666193735Srmacklem * If the "cred" argument is NULL, a new clientid should not be created. 667193735Srmacklem * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot 668193735Srmacklem * be done. 669192337Srmacklem * It always clpp with a reference count on it, unless returning an error. 670191783Srmacklem */ 671191783SrmacklemAPPLESTATIC int 672191783Srmacklemnfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, 673191783Srmacklem struct nfsclclient **clpp) 674191783Srmacklem{ 675191783Srmacklem struct nfsclclient *clp; 676193735Srmacklem struct nfsclclient *newclp = NULL; 677191783Srmacklem struct nfscllockowner *lp, *nlp; 678191783Srmacklem struct nfsmount *nmp = VFSTONFS(vnode_mount(vp)); 679193066Sjamie char uuid[HOSTUUIDLEN]; 680191783Srmacklem int igotlock = 0, error, trystalecnt, clidinusedelay, i; 681193735Srmacklem u_int16_t idlen = 0; 682191783Srmacklem 683193735Srmacklem if (cred != NULL) { 684194117Sjamie getcredhostuuid(cred, uuid, sizeof uuid); 685193735Srmacklem idlen = strlen(uuid); 686193735Srmacklem if (idlen > 0) 687193735Srmacklem idlen += sizeof (u_int64_t); 688193735Srmacklem else 689193735Srmacklem idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */ 690193735Srmacklem MALLOC(newclp, struct nfsclclient *, 691193735Srmacklem sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT, 692193735Srmacklem M_WAITOK); 693193735Srmacklem } 694191783Srmacklem NFSLOCKCLSTATE(); 695191783Srmacklem clp = nmp->nm_clp; 696191783Srmacklem if (clp == NULL) { 697193735Srmacklem if (newclp == NULL) { 698193735Srmacklem NFSUNLOCKCLSTATE(); 699193735Srmacklem return (EACCES); 700193735Srmacklem } 701191783Srmacklem clp = newclp; 702191783Srmacklem NFSBZERO((caddr_t)clp, sizeof(struct nfsclclient) + idlen - 1); 703191783Srmacklem clp->nfsc_idlen = idlen; 704191783Srmacklem LIST_INIT(&clp->nfsc_owner); 705191783Srmacklem TAILQ_INIT(&clp->nfsc_deleg); 706191783Srmacklem for (i = 0; i < NFSCLDELEGHASHSIZE; i++) 707191783Srmacklem LIST_INIT(&clp->nfsc_deleghash[i]); 708191783Srmacklem LIST_INIT(&clp->nfsc_defunctlockowner); 709191783Srmacklem clp->nfsc_flags = NFSCLFLAGS_INITED; 710191783Srmacklem clp->nfsc_clientidrev = 1; 711191783Srmacklem clp->nfsc_cbident = nfscl_nextcbident(); 712193066Sjamie nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id, 713191783Srmacklem clp->nfsc_idlen); 714191783Srmacklem LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list); 715191783Srmacklem nmp->nm_clp = clp; 716191783Srmacklem clp->nfsc_nmp = nmp; 717191783Srmacklem NFSUNLOCKCLSTATE(); 718191783Srmacklem nfscl_start_renewthread(clp); 719191783Srmacklem } else { 720191783Srmacklem NFSUNLOCKCLSTATE(); 721193735Srmacklem if (newclp != NULL) 722193735Srmacklem FREE((caddr_t)newclp, M_NFSCLCLIENT); 723191783Srmacklem } 724191783Srmacklem NFSLOCKCLSTATE(); 725191783Srmacklem while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock) 726191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 727191783Srmacklem NFSCLSTATEMUTEXPTR); 728191783Srmacklem if (!igotlock) 729191783Srmacklem nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR); 730191783Srmacklem NFSUNLOCKCLSTATE(); 731191783Srmacklem 732191783Srmacklem /* 733191783Srmacklem * If it needs a clientid, do the setclientid now. 734191783Srmacklem */ 735191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) { 736191783Srmacklem if (!igotlock) 737191783Srmacklem panic("nfscl_clget"); 738193735Srmacklem if (p == NULL || cred == NULL) { 739191783Srmacklem NFSLOCKCLSTATE(); 740191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 741191783Srmacklem NFSUNLOCKCLSTATE(); 742191783Srmacklem return (EACCES); 743191783Srmacklem } 744191783Srmacklem /* get rid of defunct lockowners */ 745191783Srmacklem LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, 746191783Srmacklem nlp) { 747191783Srmacklem nfscl_freelockowner(lp, 0); 748191783Srmacklem } 749191783Srmacklem /* 750191783Srmacklem * If RFC3530 Sec. 14.2.33 is taken literally, 751191783Srmacklem * NFSERR_CLIDINUSE will be returned persistently for the 752191783Srmacklem * case where a new mount of the same file system is using 753191783Srmacklem * a different principal. In practice, NFSERR_CLIDINUSE is 754191783Srmacklem * only returned when there is outstanding unexpired state 755191783Srmacklem * on the clientid. As such, try for twice the lease 756191783Srmacklem * interval, if we know what that is. Otherwise, make a 757191783Srmacklem * wild ass guess. 758191783Srmacklem * The case of returning NFSERR_STALECLIENTID is far less 759191783Srmacklem * likely, but might occur if there is a significant delay 760191783Srmacklem * between doing the SetClientID and SetClientIDConfirm Ops, 761191783Srmacklem * such that the server throws away the clientid before 762191783Srmacklem * receiving the SetClientIDConfirm. 763191783Srmacklem */ 764191783Srmacklem if (clp->nfsc_renew > 0) 765191783Srmacklem clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2; 766191783Srmacklem else 767191783Srmacklem clidinusedelay = 120; 768191783Srmacklem trystalecnt = 3; 769191783Srmacklem do { 770193735Srmacklem error = nfsrpc_setclient(VFSTONFS(vnode_mount(vp)), 771193735Srmacklem clp, cred, p); 772191783Srmacklem if (error == NFSERR_STALECLIENTID || 773191783Srmacklem error == NFSERR_STALEDONTRECOVER || 774191783Srmacklem error == NFSERR_CLIDINUSE) { 775191783Srmacklem (void) nfs_catnap(PZERO, "nfs_setcl"); 776191783Srmacklem } 777191783Srmacklem } while (((error == NFSERR_STALECLIENTID || 778191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) || 779191783Srmacklem (error == NFSERR_CLIDINUSE && --clidinusedelay > 0)); 780191783Srmacklem if (error) { 781191783Srmacklem NFSLOCKCLSTATE(); 782191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 783191783Srmacklem NFSUNLOCKCLSTATE(); 784191783Srmacklem return (error); 785191783Srmacklem } 786191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 787191783Srmacklem } 788191783Srmacklem if (igotlock) { 789191783Srmacklem NFSLOCKCLSTATE(); 790191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 1); 791191783Srmacklem NFSUNLOCKCLSTATE(); 792191783Srmacklem } 793191783Srmacklem 794191783Srmacklem *clpp = clp; 795191783Srmacklem return (0); 796191783Srmacklem} 797191783Srmacklem 798191783Srmacklem/* 799191783Srmacklem * Get a reference to a clientid and return it, if valid. 800191783Srmacklem */ 801191783SrmacklemAPPLESTATIC struct nfsclclient * 802191783Srmacklemnfscl_findcl(struct nfsmount *nmp) 803191783Srmacklem{ 804191783Srmacklem struct nfsclclient *clp; 805191783Srmacklem 806191783Srmacklem clp = nmp->nm_clp; 807191783Srmacklem if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) 808191783Srmacklem return (NULL); 809191783Srmacklem return (clp); 810191783Srmacklem} 811191783Srmacklem 812191783Srmacklem/* 813191783Srmacklem * Release the clientid structure. It may be locked or reference counted. 814191783Srmacklem */ 815191783Srmacklemstatic void 816191783Srmacklemnfscl_clrelease(struct nfsclclient *clp) 817191783Srmacklem{ 818191783Srmacklem 819191783Srmacklem if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK) 820191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 821191783Srmacklem else 822191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 823191783Srmacklem} 824191783Srmacklem 825191783Srmacklem/* 826191783Srmacklem * External call for nfscl_clrelease. 827191783Srmacklem */ 828191783SrmacklemAPPLESTATIC void 829191783Srmacklemnfscl_clientrelease(struct nfsclclient *clp) 830191783Srmacklem{ 831191783Srmacklem 832191783Srmacklem NFSLOCKCLSTATE(); 833191783Srmacklem if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK) 834191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 835191783Srmacklem else 836191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 837191783Srmacklem NFSUNLOCKCLSTATE(); 838191783Srmacklem} 839191783Srmacklem 840191783Srmacklem/* 841191783Srmacklem * Called when wanting to lock a byte region. 842191783Srmacklem */ 843191783SrmacklemAPPLESTATIC int 844191783Srmacklemnfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len, 845191783Srmacklem short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp, 846191783Srmacklem int recovery, u_int8_t *rownp, u_int8_t *ropenownp, 847191783Srmacklem struct nfscllockowner **lpp, int *newonep, int *donelocallyp) 848191783Srmacklem{ 849191783Srmacklem struct nfscllockowner *lp; 850191783Srmacklem struct nfsclopen *op; 851191783Srmacklem struct nfsclclient *clp; 852191783Srmacklem struct nfscllockowner *nlp; 853191783Srmacklem struct nfscllock *nlop, *otherlop; 854191783Srmacklem struct nfscldeleg *dp = NULL, *ldp = NULL; 855191783Srmacklem struct nfscllockownerhead *lhp = NULL; 856191783Srmacklem struct nfsnode *np; 857191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp; 858191783Srmacklem int error = 0, ret, donelocally = 0; 859191783Srmacklem u_int32_t mode; 860191783Srmacklem 861191783Srmacklem if (type == F_WRLCK) 862191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 863191783Srmacklem else 864191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 865191783Srmacklem np = VTONFS(vp); 866191783Srmacklem *lpp = NULL; 867191783Srmacklem *newonep = 0; 868191783Srmacklem *donelocallyp = 0; 869191783Srmacklem 870191783Srmacklem /* 871191783Srmacklem * Might need these, so MALLOC them now, to 872191783Srmacklem * avoid a tsleep() in MALLOC later. 873191783Srmacklem */ 874191783Srmacklem MALLOC(nlp, struct nfscllockowner *, 875191783Srmacklem sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK); 876191783Srmacklem MALLOC(otherlop, struct nfscllock *, 877191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 878191783Srmacklem MALLOC(nlop, struct nfscllock *, 879191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 880191783Srmacklem nlop->nfslo_type = type; 881191783Srmacklem nlop->nfslo_first = off; 882191783Srmacklem if (len == NFS64BITSSET) { 883191783Srmacklem nlop->nfslo_end = NFS64BITSSET; 884191783Srmacklem } else { 885191783Srmacklem nlop->nfslo_end = off + len; 886191783Srmacklem if (nlop->nfslo_end <= nlop->nfslo_first) 887191783Srmacklem error = NFSERR_INVAL; 888191783Srmacklem } 889191783Srmacklem 890191783Srmacklem if (!error) { 891191783Srmacklem if (recovery) 892191783Srmacklem clp = rclp; 893191783Srmacklem else 894191783Srmacklem error = nfscl_getcl(vp, cred, p, &clp); 895191783Srmacklem } 896191783Srmacklem if (error) { 897191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 898191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 899191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 900191783Srmacklem return (error); 901191783Srmacklem } 902191783Srmacklem 903191783Srmacklem op = NULL; 904191783Srmacklem if (recovery) { 905191783Srmacklem ownp = rownp; 906191783Srmacklem } else { 907191783Srmacklem nfscl_filllockowner(p, own); 908191783Srmacklem ownp = own; 909191783Srmacklem } 910191783Srmacklem if (!recovery) { 911191783Srmacklem NFSLOCKCLSTATE(); 912191783Srmacklem /* 913191783Srmacklem * First, search for a delegation. If one exists for this file, 914191783Srmacklem * the lock can be done locally against it, so long as there 915191783Srmacklem * isn't a local lock conflict. 916191783Srmacklem */ 917191783Srmacklem ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 918191783Srmacklem np->n_fhp->nfh_len); 919191783Srmacklem /* Just sanity check for correct type of delegation */ 920191783Srmacklem if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_RECALL) || 921191783Srmacklem (type == F_WRLCK && !(dp->nfsdl_flags & NFSCLDL_WRITE)))) 922191783Srmacklem dp = NULL; 923191783Srmacklem } 924191783Srmacklem if (dp != NULL) { 925191783Srmacklem /* Now, find the associated open to get the correct openowner */ 926191783Srmacklem ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh, 927191783Srmacklem np->n_fhp->nfh_len, NULL, p, mode, NULL, &op); 928191783Srmacklem if (ret) 929191783Srmacklem ret = nfscl_getopen(&clp->nfsc_owner, 930191783Srmacklem np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p, 931191783Srmacklem mode, NULL, &op); 932191783Srmacklem if (!ret) { 933191783Srmacklem lhp = &dp->nfsdl_lock; 934191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 935191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list); 936191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 937191783Srmacklem donelocally = 1; 938191783Srmacklem } else { 939191783Srmacklem dp = NULL; 940191783Srmacklem } 941191783Srmacklem } 942191783Srmacklem if (!donelocally) { 943191783Srmacklem /* 944191783Srmacklem * Get the related Open. 945191783Srmacklem */ 946191783Srmacklem if (recovery) 947191783Srmacklem error = nfscl_getopen(&clp->nfsc_owner, 948191783Srmacklem np->n_fhp->nfh_fh, np->n_fhp->nfh_len, ropenownp, 949191783Srmacklem NULL, mode, NULL, &op); 950191783Srmacklem else 951191783Srmacklem error = nfscl_getopen(&clp->nfsc_owner, 952191783Srmacklem np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p, 953191783Srmacklem mode, NULL, &op); 954191783Srmacklem if (!error) 955191783Srmacklem lhp = &op->nfso_lock; 956191783Srmacklem } 957191783Srmacklem if (!error && !recovery) 958201439Srmacklem error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, 959201439Srmacklem np->n_fhp->nfh_len, nlop, ownp, ldp, NULL); 960191783Srmacklem if (error) { 961191783Srmacklem if (!recovery) { 962191783Srmacklem nfscl_clrelease(clp); 963191783Srmacklem NFSUNLOCKCLSTATE(); 964191783Srmacklem } 965191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 966191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 967191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 968191783Srmacklem return (error); 969191783Srmacklem } 970191783Srmacklem 971191783Srmacklem /* 972191783Srmacklem * Ok, see if a lockowner exists and create one, as required. 973191783Srmacklem */ 974191783Srmacklem LIST_FOREACH(lp, lhp, nfsl_list) { 975191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN)) 976191783Srmacklem break; 977191783Srmacklem } 978191783Srmacklem if (lp == NULL) { 979191783Srmacklem NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN); 980191783Srmacklem if (recovery) 981191783Srmacklem NFSBCOPY(ropenownp, nlp->nfsl_openowner, 982191783Srmacklem NFSV4CL_LOCKNAMELEN); 983191783Srmacklem else 984191783Srmacklem NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner, 985191783Srmacklem NFSV4CL_LOCKNAMELEN); 986191783Srmacklem nlp->nfsl_seqid = 0; 987191783Srmacklem nlp->nfsl_defunct = 0; 988191783Srmacklem nlp->nfsl_inprog = NULL; 989191783Srmacklem nfscl_lockinit(&nlp->nfsl_rwlock); 990191783Srmacklem LIST_INIT(&nlp->nfsl_lock); 991191783Srmacklem if (donelocally) { 992191783Srmacklem nlp->nfsl_open = NULL; 993191783Srmacklem newnfsstats.cllocallockowners++; 994191783Srmacklem } else { 995191783Srmacklem nlp->nfsl_open = op; 996191783Srmacklem newnfsstats.cllockowners++; 997191783Srmacklem } 998191783Srmacklem LIST_INSERT_HEAD(lhp, nlp, nfsl_list); 999191783Srmacklem lp = nlp; 1000191783Srmacklem nlp = NULL; 1001191783Srmacklem *newonep = 1; 1002191783Srmacklem } 1003191783Srmacklem 1004191783Srmacklem /* 1005191783Srmacklem * Now, update the byte ranges for locks. 1006191783Srmacklem */ 1007191783Srmacklem ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally); 1008191783Srmacklem if (!ret) 1009191783Srmacklem donelocally = 1; 1010191783Srmacklem if (donelocally) { 1011191783Srmacklem *donelocallyp = 1; 1012191783Srmacklem if (!recovery) 1013191783Srmacklem nfscl_clrelease(clp); 1014191783Srmacklem } else { 1015191783Srmacklem /* 1016191783Srmacklem * Serial modifications on the lock owner for multiple threads 1017191783Srmacklem * for the same process using a read/write lock. 1018191783Srmacklem */ 1019191783Srmacklem if (!recovery) 1020191783Srmacklem nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR); 1021191783Srmacklem } 1022191783Srmacklem if (!recovery) 1023191783Srmacklem NFSUNLOCKCLSTATE(); 1024191783Srmacklem 1025191783Srmacklem if (nlp) 1026191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 1027191783Srmacklem if (nlop) 1028191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1029191783Srmacklem if (otherlop) 1030191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 1031191783Srmacklem 1032191783Srmacklem *lpp = lp; 1033191783Srmacklem return (0); 1034191783Srmacklem} 1035191783Srmacklem 1036191783Srmacklem/* 1037191783Srmacklem * Called to unlock a byte range, for LockU. 1038191783Srmacklem */ 1039191783SrmacklemAPPLESTATIC int 1040191783Srmacklemnfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len, 1041191783Srmacklem __unused struct ucred *cred, NFSPROC_T *p, int callcnt, 1042191783Srmacklem struct nfsclclient *clp, struct nfscllockowner **lpp, int *dorpcp) 1043191783Srmacklem{ 1044191783Srmacklem struct nfscllockowner *lp; 1045191783Srmacklem struct nfsclowner *owp; 1046191783Srmacklem struct nfsclopen *op; 1047191783Srmacklem struct nfscllock *nlop, *other_lop = NULL; 1048191783Srmacklem struct nfscldeleg *dp; 1049191783Srmacklem struct nfsnode *np; 1050191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1051201439Srmacklem int ret = 0, fnd; 1052191783Srmacklem 1053191783Srmacklem np = VTONFS(vp); 1054191783Srmacklem *lpp = NULL; 1055191783Srmacklem *dorpcp = 0; 1056191783Srmacklem 1057191783Srmacklem /* 1058191783Srmacklem * Might need these, so MALLOC them now, to 1059191783Srmacklem * avoid a tsleep() in MALLOC later. 1060191783Srmacklem */ 1061191783Srmacklem MALLOC(nlop, struct nfscllock *, 1062191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 1063191783Srmacklem nlop->nfslo_type = F_UNLCK; 1064191783Srmacklem nlop->nfslo_first = off; 1065191783Srmacklem if (len == NFS64BITSSET) { 1066191783Srmacklem nlop->nfslo_end = NFS64BITSSET; 1067191783Srmacklem } else { 1068191783Srmacklem nlop->nfslo_end = off + len; 1069191783Srmacklem if (nlop->nfslo_end <= nlop->nfslo_first) { 1070191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1071191783Srmacklem return (NFSERR_INVAL); 1072191783Srmacklem } 1073191783Srmacklem } 1074191783Srmacklem if (callcnt == 0) { 1075191783Srmacklem MALLOC(other_lop, struct nfscllock *, 1076191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 1077191783Srmacklem *other_lop = *nlop; 1078191783Srmacklem } 1079191783Srmacklem nfscl_filllockowner(p, own); 1080191783Srmacklem dp = NULL; 1081191783Srmacklem NFSLOCKCLSTATE(); 1082191783Srmacklem if (callcnt == 0) 1083191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 1084191783Srmacklem np->n_fhp->nfh_len); 1085191783Srmacklem 1086191783Srmacklem /* 1087191783Srmacklem * First, unlock any local regions on a delegation. 1088191783Srmacklem */ 1089191783Srmacklem if (dp != NULL) { 1090191783Srmacklem /* Look for this lockowner. */ 1091191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 1092191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1093191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1094191783Srmacklem break; 1095191783Srmacklem } 1096191783Srmacklem if (lp != NULL) 1097191783Srmacklem /* Use other_lop, so nlop is still available */ 1098191783Srmacklem (void)nfscl_updatelock(lp, &other_lop, NULL, 1); 1099191783Srmacklem } 1100191783Srmacklem 1101191783Srmacklem /* 1102191783Srmacklem * Now, find a matching open/lockowner that hasn't already been done, 1103191783Srmacklem * as marked by nfsl_inprog. 1104191783Srmacklem */ 1105191783Srmacklem lp = NULL; 1106191783Srmacklem fnd = 0; 1107191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1108191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1109191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1110191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1111191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1112191783Srmacklem if (lp->nfsl_inprog == NULL && 1113191783Srmacklem !NFSBCMP(lp->nfsl_owner, own, 1114191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1115191783Srmacklem fnd = 1; 1116191783Srmacklem break; 1117191783Srmacklem } 1118191783Srmacklem } 1119191783Srmacklem if (fnd) 1120191783Srmacklem break; 1121191783Srmacklem } 1122191783Srmacklem } 1123191783Srmacklem if (fnd) 1124191783Srmacklem break; 1125191783Srmacklem } 1126191783Srmacklem 1127191783Srmacklem if (lp != NULL) { 1128191783Srmacklem ret = nfscl_updatelock(lp, &nlop, NULL, 0); 1129191783Srmacklem if (ret) 1130191783Srmacklem *dorpcp = 1; 1131191783Srmacklem /* 1132191783Srmacklem * Serial modifications on the lock owner for multiple 1133191783Srmacklem * threads for the same process using a read/write lock. 1134191783Srmacklem */ 1135191783Srmacklem lp->nfsl_inprog = p; 1136191783Srmacklem nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR); 1137191783Srmacklem *lpp = lp; 1138191783Srmacklem } 1139191783Srmacklem NFSUNLOCKCLSTATE(); 1140191783Srmacklem if (nlop) 1141191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1142191783Srmacklem if (other_lop) 1143191783Srmacklem FREE((caddr_t)other_lop, M_NFSCLLOCK); 1144191783Srmacklem return (0); 1145191783Srmacklem} 1146191783Srmacklem 1147191783Srmacklem/* 1148191783Srmacklem * Release all lockowners marked in progess for this process and file. 1149191783Srmacklem */ 1150191783SrmacklemAPPLESTATIC void 1151191783Srmacklemnfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p) 1152191783Srmacklem{ 1153191783Srmacklem struct nfsclowner *owp; 1154191783Srmacklem struct nfsclopen *op; 1155191783Srmacklem struct nfscllockowner *lp; 1156191783Srmacklem struct nfsnode *np; 1157191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1158191783Srmacklem 1159191783Srmacklem np = VTONFS(vp); 1160191783Srmacklem nfscl_filllockowner(p, own); 1161191783Srmacklem NFSLOCKCLSTATE(); 1162191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1163191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1164191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1165191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1166191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1167191783Srmacklem if (lp->nfsl_inprog == p && 1168191783Srmacklem !NFSBCMP(lp->nfsl_owner, own, 1169191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1170191783Srmacklem lp->nfsl_inprog = NULL; 1171191783Srmacklem nfscl_lockunlock(&lp->nfsl_rwlock); 1172191783Srmacklem } 1173191783Srmacklem } 1174191783Srmacklem } 1175191783Srmacklem } 1176191783Srmacklem } 1177191783Srmacklem nfscl_clrelease(clp); 1178191783Srmacklem NFSUNLOCKCLSTATE(); 1179191783Srmacklem} 1180191783Srmacklem 1181191783Srmacklem/* 1182191783Srmacklem * Called to find out if any bytes within the byte range specified are 1183191783Srmacklem * write locked by the calling process. Used to determine if flushing 1184191783Srmacklem * is required before a LockU. 1185191783Srmacklem * If in doubt, return 1, so the flush will occur. 1186191783Srmacklem */ 1187191783SrmacklemAPPLESTATIC int 1188191783Srmacklemnfscl_checkwritelocked(vnode_t vp, struct flock *fl, 1189191783Srmacklem struct ucred *cred, NFSPROC_T *p) 1190191783Srmacklem{ 1191191783Srmacklem struct nfsclowner *owp; 1192191783Srmacklem struct nfscllockowner *lp; 1193191783Srmacklem struct nfsclopen *op; 1194191783Srmacklem struct nfsclclient *clp; 1195191783Srmacklem struct nfscllock *lop; 1196191783Srmacklem struct nfscldeleg *dp; 1197191783Srmacklem struct nfsnode *np; 1198191783Srmacklem u_int64_t off, end; 1199191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1200191783Srmacklem int error = 0; 1201191783Srmacklem 1202191783Srmacklem np = VTONFS(vp); 1203191783Srmacklem switch (fl->l_whence) { 1204191783Srmacklem case SEEK_SET: 1205191783Srmacklem case SEEK_CUR: 1206191783Srmacklem /* 1207191783Srmacklem * Caller is responsible for adding any necessary offset 1208191783Srmacklem * when SEEK_CUR is used. 1209191783Srmacklem */ 1210191783Srmacklem off = fl->l_start; 1211191783Srmacklem break; 1212191783Srmacklem case SEEK_END: 1213191783Srmacklem off = np->n_size + fl->l_start; 1214191783Srmacklem break; 1215191783Srmacklem default: 1216191783Srmacklem return (1); 1217191783Srmacklem }; 1218191783Srmacklem if (fl->l_len != 0) { 1219191783Srmacklem end = off + fl->l_len; 1220191783Srmacklem if (end < off) 1221191783Srmacklem return (1); 1222191783Srmacklem } else { 1223191783Srmacklem end = NFS64BITSSET; 1224191783Srmacklem } 1225191783Srmacklem 1226191783Srmacklem error = nfscl_getcl(vp, cred, p, &clp); 1227191783Srmacklem if (error) 1228191783Srmacklem return (1); 1229191783Srmacklem nfscl_filllockowner(p, own); 1230191783Srmacklem NFSLOCKCLSTATE(); 1231191783Srmacklem 1232191783Srmacklem /* 1233191783Srmacklem * First check the delegation locks. 1234191783Srmacklem */ 1235191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 1236191783Srmacklem if (dp != NULL) { 1237191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 1238191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1239191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1240191783Srmacklem break; 1241191783Srmacklem } 1242191783Srmacklem if (lp != NULL) { 1243191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 1244191783Srmacklem if (lop->nfslo_first >= end) 1245191783Srmacklem break; 1246191783Srmacklem if (lop->nfslo_end <= off) 1247191783Srmacklem continue; 1248191783Srmacklem if (lop->nfslo_type == F_WRLCK) { 1249191783Srmacklem nfscl_clrelease(clp); 1250191783Srmacklem NFSUNLOCKCLSTATE(); 1251191783Srmacklem return (1); 1252191783Srmacklem } 1253191783Srmacklem } 1254191783Srmacklem } 1255191783Srmacklem } 1256191783Srmacklem 1257191783Srmacklem /* 1258191783Srmacklem * Now, check state against the server. 1259191783Srmacklem */ 1260191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1261191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1262191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1263191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1264191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1265191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1266191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1267191783Srmacklem break; 1268191783Srmacklem } 1269191783Srmacklem if (lp != NULL) { 1270191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 1271191783Srmacklem if (lop->nfslo_first >= end) 1272191783Srmacklem break; 1273191783Srmacklem if (lop->nfslo_end <= off) 1274191783Srmacklem continue; 1275191783Srmacklem if (lop->nfslo_type == F_WRLCK) { 1276191783Srmacklem nfscl_clrelease(clp); 1277191783Srmacklem NFSUNLOCKCLSTATE(); 1278191783Srmacklem return (1); 1279191783Srmacklem } 1280191783Srmacklem } 1281191783Srmacklem } 1282191783Srmacklem } 1283191783Srmacklem } 1284191783Srmacklem } 1285191783Srmacklem nfscl_clrelease(clp); 1286191783Srmacklem NFSUNLOCKCLSTATE(); 1287191783Srmacklem return (0); 1288191783Srmacklem} 1289191783Srmacklem 1290191783Srmacklem/* 1291191783Srmacklem * Release a byte range lock owner structure. 1292191783Srmacklem */ 1293191783SrmacklemAPPLESTATIC void 1294191783Srmacklemnfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete) 1295191783Srmacklem{ 1296191783Srmacklem struct nfsclclient *clp; 1297191783Srmacklem 1298191783Srmacklem if (lp == NULL) 1299191783Srmacklem return; 1300191783Srmacklem NFSLOCKCLSTATE(); 1301191783Srmacklem clp = lp->nfsl_open->nfso_own->nfsow_clp; 1302191783Srmacklem if (error != 0 && candelete && 1303191783Srmacklem (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0) 1304191783Srmacklem nfscl_freelockowner(lp, 0); 1305191783Srmacklem else 1306191783Srmacklem nfscl_lockunlock(&lp->nfsl_rwlock); 1307191783Srmacklem nfscl_clrelease(clp); 1308191783Srmacklem NFSUNLOCKCLSTATE(); 1309191783Srmacklem} 1310191783Srmacklem 1311191783Srmacklem/* 1312191783Srmacklem * Free up an open structure and any associated byte range lock structures. 1313191783Srmacklem */ 1314191783SrmacklemAPPLESTATIC void 1315191783Srmacklemnfscl_freeopen(struct nfsclopen *op, int local) 1316191783Srmacklem{ 1317191783Srmacklem 1318191783Srmacklem LIST_REMOVE(op, nfso_list); 1319191783Srmacklem nfscl_freealllocks(&op->nfso_lock, local); 1320191783Srmacklem FREE((caddr_t)op, M_NFSCLOPEN); 1321191783Srmacklem if (local) 1322191783Srmacklem newnfsstats.cllocalopens--; 1323191783Srmacklem else 1324191783Srmacklem newnfsstats.clopens--; 1325191783Srmacklem} 1326191783Srmacklem 1327191783Srmacklem/* 1328191783Srmacklem * Free up all lock owners and associated locks. 1329191783Srmacklem */ 1330191783Srmacklemstatic void 1331191783Srmacklemnfscl_freealllocks(struct nfscllockownerhead *lhp, int local) 1332191783Srmacklem{ 1333191783Srmacklem struct nfscllockowner *lp, *nlp; 1334191783Srmacklem 1335191783Srmacklem LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) { 1336191783Srmacklem if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED)) 1337191783Srmacklem panic("nfscllckw"); 1338191783Srmacklem nfscl_freelockowner(lp, local); 1339191783Srmacklem } 1340191783Srmacklem} 1341191783Srmacklem 1342191783Srmacklem/* 1343191783Srmacklem * Called for an Open when NFSERR_EXPIRED is received from the server. 1344191783Srmacklem * If there are no byte range locks nor a Share Deny lost, try to do a 1345191783Srmacklem * fresh Open. Otherwise, free the open. 1346191783Srmacklem */ 1347191783Srmacklemstatic int 1348191783Srmacklemnfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op, 1349191783Srmacklem struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p) 1350191783Srmacklem{ 1351191783Srmacklem struct nfscllockowner *lp; 1352191783Srmacklem struct nfscldeleg *dp; 1353191783Srmacklem int mustdelete = 0, error; 1354191783Srmacklem 1355191783Srmacklem /* 1356191783Srmacklem * Look for any byte range lock(s). 1357191783Srmacklem */ 1358191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1359191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 1360191783Srmacklem mustdelete = 1; 1361191783Srmacklem break; 1362191783Srmacklem } 1363191783Srmacklem } 1364191783Srmacklem 1365191783Srmacklem /* 1366191783Srmacklem * If no byte range lock(s) nor a Share deny, try to re-open. 1367191783Srmacklem */ 1368191783Srmacklem if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) { 1369191783Srmacklem newnfs_copycred(&op->nfso_cred, cred); 1370191783Srmacklem dp = NULL; 1371191783Srmacklem error = nfsrpc_reopen(nmp, op->nfso_fh, 1372191783Srmacklem op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p); 1373191783Srmacklem if (error) { 1374191783Srmacklem mustdelete = 1; 1375191783Srmacklem if (dp != NULL) { 1376191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 1377191783Srmacklem dp = NULL; 1378191783Srmacklem } 1379191783Srmacklem } 1380191783Srmacklem if (dp != NULL) 1381191783Srmacklem nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh, 1382191783Srmacklem op->nfso_fhlen, cred, p, &dp); 1383191783Srmacklem } 1384191783Srmacklem 1385191783Srmacklem /* 1386191783Srmacklem * If a byte range lock or Share deny or couldn't re-open, free it. 1387191783Srmacklem */ 1388191783Srmacklem if (mustdelete) 1389191783Srmacklem nfscl_freeopen(op, 0); 1390191783Srmacklem return (mustdelete); 1391191783Srmacklem} 1392191783Srmacklem 1393191783Srmacklem/* 1394191783Srmacklem * Free up an open owner structure. 1395191783Srmacklem */ 1396191783Srmacklemstatic void 1397191783Srmacklemnfscl_freeopenowner(struct nfsclowner *owp, int local) 1398191783Srmacklem{ 1399191783Srmacklem 1400191783Srmacklem LIST_REMOVE(owp, nfsow_list); 1401191783Srmacklem FREE((caddr_t)owp, M_NFSCLOWNER); 1402191783Srmacklem if (local) 1403191783Srmacklem newnfsstats.cllocalopenowners--; 1404191783Srmacklem else 1405191783Srmacklem newnfsstats.clopenowners--; 1406191783Srmacklem} 1407191783Srmacklem 1408191783Srmacklem/* 1409191783Srmacklem * Free up a byte range lock owner structure. 1410191783Srmacklem */ 1411191783Srmacklemstatic void 1412191783Srmacklemnfscl_freelockowner(struct nfscllockowner *lp, int local) 1413191783Srmacklem{ 1414191783Srmacklem struct nfscllock *lop, *nlop; 1415191783Srmacklem 1416191783Srmacklem LIST_REMOVE(lp, nfsl_list); 1417191783Srmacklem LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) { 1418191783Srmacklem nfscl_freelock(lop, local); 1419191783Srmacklem } 1420191783Srmacklem FREE((caddr_t)lp, M_NFSCLLOCKOWNER); 1421191783Srmacklem if (local) 1422191783Srmacklem newnfsstats.cllocallockowners--; 1423191783Srmacklem else 1424191783Srmacklem newnfsstats.cllockowners--; 1425191783Srmacklem} 1426191783Srmacklem 1427191783Srmacklem/* 1428191783Srmacklem * Free up a byte range lock structure. 1429191783Srmacklem */ 1430191783SrmacklemAPPLESTATIC void 1431191783Srmacklemnfscl_freelock(struct nfscllock *lop, int local) 1432191783Srmacklem{ 1433191783Srmacklem 1434191783Srmacklem LIST_REMOVE(lop, nfslo_list); 1435191783Srmacklem FREE((caddr_t)lop, M_NFSCLLOCK); 1436191783Srmacklem if (local) 1437191783Srmacklem newnfsstats.cllocallocks--; 1438191783Srmacklem else 1439191783Srmacklem newnfsstats.cllocks--; 1440191783Srmacklem} 1441191783Srmacklem 1442191783Srmacklem/* 1443191783Srmacklem * Clean out the state related to a delegation. 1444191783Srmacklem */ 1445191783Srmacklemstatic void 1446191783Srmacklemnfscl_cleandeleg(struct nfscldeleg *dp) 1447191783Srmacklem{ 1448191783Srmacklem struct nfsclowner *owp, *nowp; 1449191783Srmacklem struct nfsclopen *op; 1450191783Srmacklem 1451191783Srmacklem LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) { 1452191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1453191783Srmacklem if (op != NULL) { 1454191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 1455191783Srmacklem panic("nfscleandel"); 1456191783Srmacklem nfscl_freeopen(op, 1); 1457191783Srmacklem } 1458191783Srmacklem nfscl_freeopenowner(owp, 1); 1459191783Srmacklem } 1460191783Srmacklem nfscl_freealllocks(&dp->nfsdl_lock, 1); 1461191783Srmacklem} 1462191783Srmacklem 1463191783Srmacklem/* 1464191783Srmacklem * Free a delegation. 1465191783Srmacklem */ 1466191783Srmacklemstatic void 1467191783Srmacklemnfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp) 1468191783Srmacklem{ 1469191783Srmacklem 1470191783Srmacklem TAILQ_REMOVE(hdp, dp, nfsdl_list); 1471191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 1472191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 1473191783Srmacklem newnfsstats.cldelegates--; 1474191783Srmacklem nfscl_delegcnt--; 1475191783Srmacklem} 1476191783Srmacklem 1477191783Srmacklem/* 1478191783Srmacklem * Free up all state related to this client structure. 1479191783Srmacklem */ 1480191783Srmacklemstatic void 1481191783Srmacklemnfscl_cleanclient(struct nfsclclient *clp) 1482191783Srmacklem{ 1483191783Srmacklem struct nfsclowner *owp, *nowp; 1484191783Srmacklem struct nfsclopen *op, *nop; 1485191783Srmacklem struct nfscllockowner *lp, *nlp; 1486191783Srmacklem 1487191783Srmacklem 1488191783Srmacklem /* get rid of defunct lockowners */ 1489191783Srmacklem LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, nlp) { 1490191783Srmacklem nfscl_freelockowner(lp, 0); 1491191783Srmacklem } 1492191783Srmacklem 1493191783Srmacklem /* Now, all the OpenOwners, etc. */ 1494191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1495191783Srmacklem LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) { 1496191783Srmacklem nfscl_freeopen(op, 0); 1497191783Srmacklem } 1498191783Srmacklem nfscl_freeopenowner(owp, 0); 1499191783Srmacklem } 1500191783Srmacklem} 1501191783Srmacklem 1502191783Srmacklem/* 1503191783Srmacklem * Called when an NFSERR_EXPIRED is received from the server. 1504191783Srmacklem */ 1505191783Srmacklemstatic void 1506191783Srmacklemnfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp, 1507191783Srmacklem struct ucred *cred, NFSPROC_T *p) 1508191783Srmacklem{ 1509191783Srmacklem struct nfsclowner *owp, *nowp, *towp; 1510191783Srmacklem struct nfsclopen *op, *nop, *top; 1511191783Srmacklem struct nfscldeleg *dp, *ndp; 1512191783Srmacklem int ret, printed = 0; 1513191783Srmacklem 1514191783Srmacklem /* 1515191783Srmacklem * First, merge locally issued Opens into the list for the server. 1516191783Srmacklem */ 1517191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 1518191783Srmacklem while (dp != NULL) { 1519191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 1520191783Srmacklem owp = LIST_FIRST(&dp->nfsdl_owner); 1521191783Srmacklem while (owp != NULL) { 1522191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1523191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1524191783Srmacklem if (op != NULL) { 1525191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 1526191783Srmacklem panic("nfsclexp"); 1527191783Srmacklem LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) { 1528191783Srmacklem if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner, 1529191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1530191783Srmacklem break; 1531191783Srmacklem } 1532191783Srmacklem if (towp != NULL) { 1533191783Srmacklem /* Merge opens in */ 1534191783Srmacklem LIST_FOREACH(top, &towp->nfsow_open, nfso_list) { 1535191783Srmacklem if (top->nfso_fhlen == op->nfso_fhlen && 1536191783Srmacklem !NFSBCMP(top->nfso_fh, op->nfso_fh, 1537191783Srmacklem op->nfso_fhlen)) { 1538191783Srmacklem top->nfso_mode |= op->nfso_mode; 1539191783Srmacklem top->nfso_opencnt += op->nfso_opencnt; 1540191783Srmacklem break; 1541191783Srmacklem } 1542191783Srmacklem } 1543191783Srmacklem if (top == NULL) { 1544191783Srmacklem /* Just add the open to the owner list */ 1545191783Srmacklem LIST_REMOVE(op, nfso_list); 1546191783Srmacklem op->nfso_own = towp; 1547191783Srmacklem LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list); 1548191783Srmacklem newnfsstats.cllocalopens--; 1549191783Srmacklem newnfsstats.clopens++; 1550191783Srmacklem } 1551191783Srmacklem } else { 1552191783Srmacklem /* Just add the openowner to the client list */ 1553191783Srmacklem LIST_REMOVE(owp, nfsow_list); 1554191783Srmacklem owp->nfsow_clp = clp; 1555191783Srmacklem LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list); 1556191783Srmacklem newnfsstats.cllocalopenowners--; 1557191783Srmacklem newnfsstats.clopenowners++; 1558191783Srmacklem newnfsstats.cllocalopens--; 1559191783Srmacklem newnfsstats.clopens++; 1560191783Srmacklem } 1561191783Srmacklem } 1562191783Srmacklem owp = nowp; 1563191783Srmacklem } 1564191783Srmacklem if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) { 1565191783Srmacklem printed = 1; 1566191783Srmacklem printf("nfsv4 expired locks lost\n"); 1567191783Srmacklem } 1568191783Srmacklem nfscl_cleandeleg(dp); 1569191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 1570191783Srmacklem dp = ndp; 1571191783Srmacklem } 1572191783Srmacklem if (!TAILQ_EMPTY(&clp->nfsc_deleg)) 1573191783Srmacklem panic("nfsclexp"); 1574191783Srmacklem 1575191783Srmacklem /* 1576191783Srmacklem * Now, try and reopen against the server. 1577191783Srmacklem */ 1578191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1579191783Srmacklem owp->nfsow_seqid = 0; 1580191783Srmacklem LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) { 1581191783Srmacklem ret = nfscl_expireopen(clp, op, nmp, cred, p); 1582191783Srmacklem if (ret && !printed) { 1583191783Srmacklem printed = 1; 1584191783Srmacklem printf("nfsv4 expired locks lost\n"); 1585191783Srmacklem } 1586191783Srmacklem } 1587191783Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) 1588191783Srmacklem nfscl_freeopenowner(owp, 0); 1589191783Srmacklem } 1590191783Srmacklem} 1591191783Srmacklem 1592191783Srmacklem#ifndef __FreeBSD__ 1593191783Srmacklem/* 1594191783Srmacklem * Called from exit() upon process termination. 1595191783Srmacklem */ 1596191783SrmacklemAPPLESTATIC void 1597191783Srmacklemnfscl_cleanup(NFSPROC_T *p) 1598191783Srmacklem{ 1599191783Srmacklem struct nfsclclient *clp; 1600191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1601191783Srmacklem 1602191783Srmacklem if (!nfscl_inited) 1603191783Srmacklem return; 1604191783Srmacklem nfscl_filllockowner(p, own); 1605191783Srmacklem 1606191783Srmacklem NFSLOCKCLSTATE(); 1607191783Srmacklem /* 1608191783Srmacklem * Loop through all the clientids, looking for the OpenOwners. 1609191783Srmacklem */ 1610191783Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) 1611191783Srmacklem nfscl_cleanup_common(clp, own); 1612191783Srmacklem NFSUNLOCKCLSTATE(); 1613191783Srmacklem} 1614191783Srmacklem#endif /* !__FreeBSD__ */ 1615191783Srmacklem 1616191783Srmacklem/* 1617191783Srmacklem * Common code used by nfscl_cleanup() and nfscl_cleanupkext(). 1618191783Srmacklem * Must be called with CLSTATE lock held. 1619191783Srmacklem */ 1620191783Srmacklemstatic void 1621191783Srmacklemnfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own) 1622191783Srmacklem{ 1623191783Srmacklem struct nfsclowner *owp, *nowp; 1624191783Srmacklem struct nfsclopen *op; 1625191783Srmacklem struct nfscllockowner *lp, *nlp; 1626191783Srmacklem struct nfscldeleg *dp; 1627191783Srmacklem 1628191783Srmacklem /* First, get rid of local locks on delegations. */ 1629191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 1630191783Srmacklem LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) { 1631191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) { 1632191783Srmacklem if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED)) 1633191783Srmacklem panic("nfscllckw"); 1634191783Srmacklem nfscl_freelockowner(lp, 1); 1635191783Srmacklem } 1636191783Srmacklem } 1637191783Srmacklem } 1638191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 1639191783Srmacklem while (owp != NULL) { 1640191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1641191783Srmacklem if (!NFSBCMP(owp->nfsow_owner, own, 1642191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1643191783Srmacklem /* 1644191783Srmacklem * If there are children that haven't closed the 1645191783Srmacklem * file descriptors yet, the opens will still be 1646191783Srmacklem * here. For that case, let the renew thread clear 1647191783Srmacklem * out the OpenOwner later. 1648191783Srmacklem */ 1649191783Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) 1650191783Srmacklem nfscl_freeopenowner(owp, 0); 1651191783Srmacklem else 1652191783Srmacklem owp->nfsow_defunct = 1; 1653191783Srmacklem } else { 1654191783Srmacklem /* look for lockowners on other opens */ 1655191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1656191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1657191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1658191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1659191783Srmacklem lp->nfsl_defunct = 1; 1660191783Srmacklem } 1661191783Srmacklem } 1662191783Srmacklem } 1663191783Srmacklem owp = nowp; 1664191783Srmacklem } 1665191783Srmacklem 1666191783Srmacklem /* and check the defunct list */ 1667191783Srmacklem LIST_FOREACH(lp, &clp->nfsc_defunctlockowner, nfsl_list) { 1668191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) 1669191783Srmacklem lp->nfsl_defunct = 1; 1670191783Srmacklem } 1671191783Srmacklem} 1672191783Srmacklem 1673191783Srmacklem#if defined(APPLEKEXT) || defined(__FreeBSD__) 1674191783Srmacklem/* 1675191783Srmacklem * Simulate the call nfscl_cleanup() by looking for open owners associated 1676191783Srmacklem * with processes that no longer exist, since a call to nfscl_cleanup() 1677191783Srmacklem * can't be patched into exit(). 1678191783Srmacklem */ 1679191783Srmacklemstatic void 1680191783Srmacklemnfscl_cleanupkext(struct nfsclclient *clp) 1681191783Srmacklem{ 1682191783Srmacklem struct nfsclowner *owp, *nowp; 1683191783Srmacklem struct nfscllockowner *lp; 1684191783Srmacklem 1685191783Srmacklem NFSPROCLISTLOCK(); 1686191783Srmacklem NFSLOCKCLSTATE(); 1687191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1688191783Srmacklem if (nfscl_procdoesntexist(owp->nfsow_owner)) 1689191783Srmacklem nfscl_cleanup_common(clp, owp->nfsow_owner); 1690191783Srmacklem } 1691191783Srmacklem 1692191783Srmacklem /* and check the defunct list */ 1693191783Srmacklem LIST_FOREACH(lp, &clp->nfsc_defunctlockowner, nfsl_list) { 1694191783Srmacklem if (nfscl_procdoesntexist(lp->nfsl_owner)) 1695191783Srmacklem lp->nfsl_defunct = 1; 1696191783Srmacklem } 1697191783Srmacklem NFSUNLOCKCLSTATE(); 1698191783Srmacklem NFSPROCLISTUNLOCK(); 1699191783Srmacklem} 1700191783Srmacklem#endif /* APPLEKEXT || __FreeBSD__ */ 1701191783Srmacklem 1702191783Srmacklem/* 1703191783Srmacklem * Called from nfs umount to free up the clientid. 1704191783Srmacklem */ 1705191783SrmacklemAPPLESTATIC void 1706191783Srmacklemnfscl_umount(struct nfsmount *nmp, NFSPROC_T *p) 1707191783Srmacklem{ 1708191783Srmacklem struct nfsclclient *clp; 1709191783Srmacklem struct ucred *cred; 1710191783Srmacklem int igotlock; 1711191783Srmacklem 1712191783Srmacklem clp = nmp->nm_clp; 1713191783Srmacklem if (clp != NULL) { 1714191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0) 1715191783Srmacklem panic("nfscl umount"); 1716191783Srmacklem 1717191783Srmacklem /* 1718191783Srmacklem * First, handshake with the nfscl renew thread, to terminate 1719191783Srmacklem * it. 1720191783Srmacklem */ 1721191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_UMOUNT; 1722191783Srmacklem while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD) 1723191783Srmacklem (void) tsleep((caddr_t)clp, PWAIT, "nfsclumnt", hz); 1724191783Srmacklem 1725191783Srmacklem NFSLOCKCLSTATE(); 1726191783Srmacklem do { 1727191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 1728191783Srmacklem NFSCLSTATEMUTEXPTR); 1729191783Srmacklem } while (!igotlock); 1730191783Srmacklem NFSUNLOCKCLSTATE(); 1731191783Srmacklem 1732191783Srmacklem /* 1733191783Srmacklem * Free up all the state. It will expire on the server, but 1734191783Srmacklem * maybe we should do a SetClientId/SetClientIdConfirm so 1735191783Srmacklem * the server throws it away? 1736191783Srmacklem */ 1737191783Srmacklem LIST_REMOVE(clp, nfsc_list); 1738191783Srmacklem nfscl_delegreturnall(clp, p); 1739191783Srmacklem cred = newnfs_getcred(); 1740191783Srmacklem (void) nfsrpc_setclient(nmp, clp, cred, p); 1741191783Srmacklem nfscl_cleanclient(clp); 1742191783Srmacklem nmp->nm_clp = NULL; 1743191783Srmacklem NFSFREECRED(cred); 1744191783Srmacklem FREE((caddr_t)clp, M_NFSCLCLIENT); 1745191783Srmacklem } 1746191783Srmacklem 1747191783Srmacklem} 1748191783Srmacklem 1749191783Srmacklem/* 1750191783Srmacklem * This function is called when a server replies with NFSERR_STALECLIENTID 1751191783Srmacklem * or NFSERR_STALESTATEID. It traverses the clientid lists, doing Opens 1752191783Srmacklem * and Locks with reclaim. If these fail, it deletes the corresponding state. 1753191783Srmacklem */ 1754191783Srmacklemstatic void 1755191783Srmacklemnfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p) 1756191783Srmacklem{ 1757191783Srmacklem struct nfsclowner *owp, *nowp; 1758191783Srmacklem struct nfsclopen *op, *nop; 1759191783Srmacklem struct nfscllockowner *lp, *nlp; 1760191783Srmacklem struct nfscllock *lop, *nlop; 1761191783Srmacklem struct nfscldeleg *dp, *ndp, *tdp; 1762191783Srmacklem struct nfsmount *nmp; 1763191783Srmacklem struct ucred *tcred; 1764191783Srmacklem struct nfsclopenhead extra_open; 1765191783Srmacklem struct nfscldeleghead extra_deleg; 1766191783Srmacklem struct nfsreq *rep; 1767191783Srmacklem u_int64_t len; 1768191783Srmacklem u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode; 1769191783Srmacklem int igotlock = 0, error, trycnt, firstlock, s; 1770191783Srmacklem 1771191783Srmacklem /* 1772191783Srmacklem * First, lock the client structure, so everyone else will 1773191783Srmacklem * block when trying to use state. 1774191783Srmacklem */ 1775191783Srmacklem NFSLOCKCLSTATE(); 1776191783Srmacklem do { 1777191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 1778191783Srmacklem NFSCLSTATEMUTEXPTR); 1779191783Srmacklem } while (!igotlock); 1780191783Srmacklem NFSUNLOCKCLSTATE(); 1781191783Srmacklem 1782191783Srmacklem nmp = clp->nfsc_nmp; 1783191783Srmacklem if (nmp == NULL) 1784191783Srmacklem panic("nfscl recover"); 1785191783Srmacklem trycnt = 5; 1786191783Srmacklem do { 1787191783Srmacklem error = nfsrpc_setclient(nmp, clp, cred, p); 1788191783Srmacklem } while ((error == NFSERR_STALECLIENTID || 1789191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); 1790191783Srmacklem if (error) { 1791191783Srmacklem nfscl_cleanclient(clp); 1792191783Srmacklem clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID | 1793191783Srmacklem NFSCLFLAGS_RECOVER); 1794191783Srmacklem NFSLOCKCLSTATE(); 1795191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 1796191783Srmacklem NFSUNLOCKCLSTATE(); 1797191783Srmacklem return; 1798191783Srmacklem } 1799191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 1800191783Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER; 1801191783Srmacklem 1802191783Srmacklem /* 1803191783Srmacklem * Mark requests already queued on the server, so that they don't 1804191783Srmacklem * initiate another recovery cycle. Any requests already in the 1805191783Srmacklem * queue that handle state information will have the old stale 1806191783Srmacklem * clientid/stateid and will get a NFSERR_STALESTATEID or 1807191783Srmacklem * NFSERR_STALECLIENTID reply from the server. This will be 1808191783Srmacklem * translated to NFSERR_STALEDONTRECOVER when R_DONTRECOVER is set. 1809191783Srmacklem */ 1810191783Srmacklem s = splsoftclock(); 1811191783Srmacklem NFSLOCKREQ(); 1812191783Srmacklem TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) { 1813191783Srmacklem if (rep->r_nmp == nmp) 1814191783Srmacklem rep->r_flags |= R_DONTRECOVER; 1815191783Srmacklem } 1816191783Srmacklem NFSUNLOCKREQ(); 1817191783Srmacklem splx(s); 1818191783Srmacklem 1819191783Srmacklem /* get rid of defunct lockowners */ 1820191783Srmacklem LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, nlp) { 1821191783Srmacklem nfscl_freelockowner(lp, 0); 1822191783Srmacklem } 1823191783Srmacklem 1824191783Srmacklem /* 1825191783Srmacklem * Now, mark all delegations "need reclaim". 1826191783Srmacklem */ 1827191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) 1828191783Srmacklem dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM; 1829191783Srmacklem 1830191783Srmacklem TAILQ_INIT(&extra_deleg); 1831191783Srmacklem LIST_INIT(&extra_open); 1832191783Srmacklem /* 1833191783Srmacklem * Now traverse the state lists, doing Open and Lock Reclaims. 1834191783Srmacklem */ 1835191783Srmacklem tcred = newnfs_getcred(); 1836191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 1837191783Srmacklem while (owp != NULL) { 1838191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1839191783Srmacklem owp->nfsow_seqid = 0; 1840191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1841191783Srmacklem while (op != NULL) { 1842191783Srmacklem nop = LIST_NEXT(op, nfso_list); 1843191783Srmacklem if (error != NFSERR_NOGRACE) { 1844191783Srmacklem /* Search for a delegation to reclaim with the open */ 1845191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 1846191783Srmacklem if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) 1847191783Srmacklem continue; 1848191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE)) { 1849191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 1850191783Srmacklem delegtype = NFSV4OPEN_DELEGATEWRITE; 1851191783Srmacklem } else { 1852191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 1853191783Srmacklem delegtype = NFSV4OPEN_DELEGATEREAD; 1854191783Srmacklem } 1855191783Srmacklem if ((op->nfso_mode & mode) == mode && 1856191783Srmacklem op->nfso_fhlen == dp->nfsdl_fhlen && 1857191783Srmacklem !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen)) 1858191783Srmacklem break; 1859191783Srmacklem } 1860191783Srmacklem ndp = dp; 1861191783Srmacklem if (dp == NULL) 1862191783Srmacklem delegtype = NFSV4OPEN_DELEGATENONE; 1863191783Srmacklem newnfs_copycred(&op->nfso_cred, tcred); 1864191783Srmacklem error = nfscl_tryopen(nmp, NULL, op->nfso_fh, 1865191783Srmacklem op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen, 1866191783Srmacklem op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype, 1867191783Srmacklem tcred, p); 1868191783Srmacklem if (!error) { 1869191783Srmacklem /* Handle any replied delegation */ 1870191783Srmacklem if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE) 1871191783Srmacklem || NFSMNT_RDONLY(nmp->nm_mountp))) { 1872191783Srmacklem if ((ndp->nfsdl_flags & NFSCLDL_WRITE)) 1873191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 1874191783Srmacklem else 1875191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 1876191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 1877191783Srmacklem if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) 1878191783Srmacklem continue; 1879191783Srmacklem if ((op->nfso_mode & mode) == mode && 1880191783Srmacklem op->nfso_fhlen == dp->nfsdl_fhlen && 1881191783Srmacklem !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, 1882191783Srmacklem op->nfso_fhlen)) { 1883191783Srmacklem dp->nfsdl_stateid = ndp->nfsdl_stateid; 1884191783Srmacklem dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit; 1885191783Srmacklem dp->nfsdl_ace = ndp->nfsdl_ace; 1886191783Srmacklem dp->nfsdl_change = ndp->nfsdl_change; 1887191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM; 1888191783Srmacklem if ((ndp->nfsdl_flags & NFSCLDL_RECALL)) 1889191783Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 1890191783Srmacklem FREE((caddr_t)ndp, M_NFSCLDELEG); 1891191783Srmacklem ndp = NULL; 1892191783Srmacklem break; 1893191783Srmacklem } 1894191783Srmacklem } 1895191783Srmacklem } 1896191783Srmacklem if (ndp != NULL) 1897191783Srmacklem TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list); 1898191783Srmacklem 1899191783Srmacklem /* and reclaim all byte range locks */ 1900191783Srmacklem lp = LIST_FIRST(&op->nfso_lock); 1901191783Srmacklem while (lp != NULL) { 1902191783Srmacklem nlp = LIST_NEXT(lp, nfsl_list); 1903191783Srmacklem lp->nfsl_seqid = 0; 1904191783Srmacklem firstlock = 1; 1905191783Srmacklem lop = LIST_FIRST(&lp->nfsl_lock); 1906191783Srmacklem while (lop != NULL) { 1907191783Srmacklem nlop = LIST_NEXT(lop, nfslo_list); 1908191783Srmacklem if (lop->nfslo_end == NFS64BITSSET) 1909191783Srmacklem len = NFS64BITSSET; 1910191783Srmacklem else 1911191783Srmacklem len = lop->nfslo_end - lop->nfslo_first; 1912191783Srmacklem if (error != NFSERR_NOGRACE) 1913191783Srmacklem error = nfscl_trylock(nmp, NULL, 1914191783Srmacklem op->nfso_fh, op->nfso_fhlen, lp, 1915191783Srmacklem firstlock, 1, lop->nfslo_first, len, 1916191783Srmacklem lop->nfslo_type, tcred, p); 1917191783Srmacklem if (error != 0) 1918191783Srmacklem nfscl_freelock(lop, 0); 1919191783Srmacklem else 1920191783Srmacklem firstlock = 0; 1921191783Srmacklem lop = nlop; 1922191783Srmacklem } 1923191783Srmacklem /* If no locks, but a lockowner, just delete it. */ 1924191783Srmacklem if (LIST_EMPTY(&lp->nfsl_lock)) 1925191783Srmacklem nfscl_freelockowner(lp, 0); 1926191783Srmacklem lp = nlp; 1927191783Srmacklem } 1928191783Srmacklem } else { 1929191783Srmacklem nfscl_freeopen(op, 0); 1930191783Srmacklem } 1931191783Srmacklem } 1932191783Srmacklem op = nop; 1933191783Srmacklem } 1934191783Srmacklem owp = nowp; 1935191783Srmacklem } 1936191783Srmacklem 1937191783Srmacklem /* 1938191783Srmacklem * Now, try and get any delegations not yet reclaimed by cobbling 1939191783Srmacklem * to-gether an appropriate open. 1940191783Srmacklem */ 1941191783Srmacklem nowp = NULL; 1942191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 1943191783Srmacklem while (dp != NULL) { 1944191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 1945191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) { 1946191783Srmacklem if (nowp == NULL) { 1947191783Srmacklem MALLOC(nowp, struct nfsclowner *, 1948191783Srmacklem sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK); 1949191783Srmacklem /* 1950191783Srmacklem * Name must be as long an largest possible 1951191783Srmacklem * NFSV4CL_LOCKNAMELEN. 12 for now. 1952191783Srmacklem */ 1953191783Srmacklem NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner, 1954191783Srmacklem NFSV4CL_LOCKNAMELEN); 1955191783Srmacklem LIST_INIT(&nowp->nfsow_open); 1956191783Srmacklem nowp->nfsow_clp = clp; 1957191783Srmacklem nowp->nfsow_seqid = 0; 1958191783Srmacklem nowp->nfsow_defunct = 0; 1959191783Srmacklem nfscl_lockinit(&nowp->nfsow_rwlock); 1960191783Srmacklem } 1961191783Srmacklem nop = NULL; 1962191783Srmacklem if (error != NFSERR_NOGRACE) { 1963191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 1964191783Srmacklem dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK); 1965191783Srmacklem nop->nfso_own = nowp; 1966191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE)) { 1967191783Srmacklem nop->nfso_mode = NFSV4OPEN_ACCESSWRITE; 1968191783Srmacklem delegtype = NFSV4OPEN_DELEGATEWRITE; 1969191783Srmacklem } else { 1970191783Srmacklem nop->nfso_mode = NFSV4OPEN_ACCESSREAD; 1971191783Srmacklem delegtype = NFSV4OPEN_DELEGATEREAD; 1972191783Srmacklem } 1973191783Srmacklem nop->nfso_opencnt = 0; 1974191783Srmacklem nop->nfso_posixlock = 1; 1975191783Srmacklem nop->nfso_fhlen = dp->nfsdl_fhlen; 1976191783Srmacklem NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen); 1977191783Srmacklem LIST_INIT(&nop->nfso_lock); 1978191783Srmacklem nop->nfso_stateid.seqid = 0; 1979191783Srmacklem nop->nfso_stateid.other[0] = 0; 1980191783Srmacklem nop->nfso_stateid.other[1] = 0; 1981191783Srmacklem nop->nfso_stateid.other[2] = 0; 1982191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, tcred); 1983191783Srmacklem newnfs_copyincred(tcred, &nop->nfso_cred); 1984191783Srmacklem tdp = NULL; 1985191783Srmacklem error = nfscl_tryopen(nmp, NULL, nop->nfso_fh, 1986191783Srmacklem nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen, 1987191783Srmacklem nop->nfso_mode, nop, NULL, 0, &tdp, 1, 1988191783Srmacklem delegtype, tcred, p); 1989191783Srmacklem if (tdp != NULL) { 1990191783Srmacklem if ((tdp->nfsdl_flags & NFSCLDL_WRITE)) 1991191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 1992191783Srmacklem else 1993191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 1994191783Srmacklem if ((nop->nfso_mode & mode) == mode && 1995191783Srmacklem nop->nfso_fhlen == tdp->nfsdl_fhlen && 1996191783Srmacklem !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh, 1997191783Srmacklem nop->nfso_fhlen)) { 1998191783Srmacklem dp->nfsdl_stateid = tdp->nfsdl_stateid; 1999191783Srmacklem dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit; 2000191783Srmacklem dp->nfsdl_ace = tdp->nfsdl_ace; 2001191783Srmacklem dp->nfsdl_change = tdp->nfsdl_change; 2002191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM; 2003191783Srmacklem if ((tdp->nfsdl_flags & NFSCLDL_RECALL)) 2004191783Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 2005191783Srmacklem FREE((caddr_t)tdp, M_NFSCLDELEG); 2006191783Srmacklem } else { 2007191783Srmacklem TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list); 2008191783Srmacklem } 2009191783Srmacklem } 2010191783Srmacklem } 2011191783Srmacklem if (error) { 2012191783Srmacklem if (nop != NULL) 2013191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 2014191783Srmacklem /* 2015191783Srmacklem * Couldn't reclaim it, so throw the state 2016191783Srmacklem * away. Ouch!! 2017191783Srmacklem */ 2018191783Srmacklem nfscl_cleandeleg(dp); 2019191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 2020191783Srmacklem } else { 2021191783Srmacklem LIST_INSERT_HEAD(&extra_open, nop, nfso_list); 2022191783Srmacklem } 2023191783Srmacklem } 2024191783Srmacklem dp = ndp; 2025191783Srmacklem } 2026191783Srmacklem 2027191783Srmacklem /* 2028191783Srmacklem * Now, get rid of extra Opens and Delegations. 2029191783Srmacklem */ 2030191783Srmacklem LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) { 2031191783Srmacklem do { 2032191783Srmacklem newnfs_copycred(&op->nfso_cred, tcred); 2033191783Srmacklem error = nfscl_tryclose(op, tcred, nmp, p); 2034191783Srmacklem if (error == NFSERR_GRACE) 2035191783Srmacklem (void) nfs_catnap(PZERO, "nfsexcls"); 2036191783Srmacklem } while (error == NFSERR_GRACE); 2037191783Srmacklem LIST_REMOVE(op, nfso_list); 2038191783Srmacklem FREE((caddr_t)op, M_NFSCLOPEN); 2039191783Srmacklem } 2040191783Srmacklem if (nowp != NULL) 2041191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 2042191783Srmacklem 2043191783Srmacklem TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) { 2044191783Srmacklem do { 2045191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, tcred); 2046191783Srmacklem error = nfscl_trydelegreturn(dp, tcred, nmp, p); 2047191783Srmacklem if (error == NFSERR_GRACE) 2048191783Srmacklem (void) nfs_catnap(PZERO, "nfsexdlg"); 2049191783Srmacklem } while (error == NFSERR_GRACE); 2050191783Srmacklem TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list); 2051191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 2052191783Srmacklem } 2053191783Srmacklem 2054191783Srmacklem NFSLOCKCLSTATE(); 2055191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2056191783Srmacklem NFSUNLOCKCLSTATE(); 2057191783Srmacklem NFSFREECRED(tcred); 2058191783Srmacklem} 2059191783Srmacklem 2060191783Srmacklem/* 2061191783Srmacklem * This function is called when a server replies with NFSERR_EXPIRED. 2062191783Srmacklem * It deletes all state for the client and does a fresh SetClientId/confirm. 2063191783Srmacklem * XXX Someday it should post a signal to the process(es) that hold the 2064191783Srmacklem * state, so they know that lock state has been lost. 2065191783Srmacklem */ 2066191783SrmacklemAPPLESTATIC int 2067191783Srmacklemnfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p) 2068191783Srmacklem{ 2069191783Srmacklem struct nfscllockowner *lp, *nlp; 2070191783Srmacklem struct nfsmount *nmp; 2071191783Srmacklem struct ucred *cred; 2072191783Srmacklem int igotlock = 0, error, trycnt; 2073191783Srmacklem 2074191783Srmacklem /* 2075191783Srmacklem * If the clientid has gone away or a new SetClientid has already 2076191783Srmacklem * been done, just return ok. 2077191783Srmacklem */ 2078191783Srmacklem if (clp == NULL || clidrev != clp->nfsc_clientidrev) 2079191783Srmacklem return (0); 2080191783Srmacklem 2081191783Srmacklem /* 2082191783Srmacklem * First, lock the client structure, so everyone else will 2083191783Srmacklem * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so 2084191783Srmacklem * that only one thread does the work. 2085191783Srmacklem */ 2086191783Srmacklem NFSLOCKCLSTATE(); 2087191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT; 2088191783Srmacklem do { 2089191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 2090191783Srmacklem NFSCLSTATEMUTEXPTR); 2091191783Srmacklem } while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT)); 2092191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) { 2093191783Srmacklem if (igotlock) 2094191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2095191783Srmacklem NFSUNLOCKCLSTATE(); 2096191783Srmacklem return (0); 2097191783Srmacklem } 2098191783Srmacklem NFSUNLOCKCLSTATE(); 2099191783Srmacklem 2100191783Srmacklem nmp = clp->nfsc_nmp; 2101191783Srmacklem if (nmp == NULL) 2102191783Srmacklem panic("nfscl expired"); 2103191783Srmacklem cred = newnfs_getcred(); 2104191783Srmacklem trycnt = 5; 2105191783Srmacklem do { 2106191783Srmacklem error = nfsrpc_setclient(nmp, clp, cred, p); 2107191783Srmacklem } while ((error == NFSERR_STALECLIENTID || 2108191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); 2109191783Srmacklem if (error) { 2110191783Srmacklem /* 2111191783Srmacklem * Clear out any state. 2112191783Srmacklem */ 2113191783Srmacklem nfscl_cleanclient(clp); 2114191783Srmacklem clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID | 2115191783Srmacklem NFSCLFLAGS_RECOVER); 2116191783Srmacklem } else { 2117191783Srmacklem /* get rid of defunct lockowners */ 2118191783Srmacklem LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, 2119191783Srmacklem nlp) { 2120191783Srmacklem nfscl_freelockowner(lp, 0); 2121191783Srmacklem } 2122191783Srmacklem 2123191783Srmacklem /* 2124191783Srmacklem * Expire the state for the client. 2125191783Srmacklem */ 2126191783Srmacklem nfscl_expireclient(clp, nmp, cred, p); 2127191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 2128191783Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER; 2129191783Srmacklem } 2130191783Srmacklem NFSFREECRED(cred); 2131191783Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_EXPIREIT; 2132191783Srmacklem NFSLOCKCLSTATE(); 2133191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2134191783Srmacklem NFSUNLOCKCLSTATE(); 2135191783Srmacklem return (error); 2136191783Srmacklem} 2137191783Srmacklem 2138191783Srmacklem/* 2139191783Srmacklem * This function inserts a lock in the list after insert_lop. 2140191783Srmacklem */ 2141191783Srmacklemstatic void 2142191783Srmacklemnfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop, 2143191783Srmacklem struct nfscllock *insert_lop, int local) 2144191783Srmacklem{ 2145191783Srmacklem 2146191783Srmacklem if ((struct nfscllockowner *)insert_lop == lp) 2147191783Srmacklem LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list); 2148191783Srmacklem else 2149191783Srmacklem LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list); 2150191783Srmacklem if (local) 2151191783Srmacklem newnfsstats.cllocallocks++; 2152191783Srmacklem else 2153191783Srmacklem newnfsstats.cllocks++; 2154191783Srmacklem} 2155191783Srmacklem 2156191783Srmacklem/* 2157191783Srmacklem * This function updates the locking for a lock owner and given file. It 2158191783Srmacklem * maintains a list of lock ranges ordered on increasing file offset that 2159191783Srmacklem * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style). 2160191783Srmacklem * It always adds new_lop to the list and sometimes uses the one pointed 2161191783Srmacklem * at by other_lopp. 2162191783Srmacklem * Returns 1 if the locks were modified, 0 otherwise. 2163191783Srmacklem */ 2164191783Srmacklemstatic int 2165191783Srmacklemnfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp, 2166191783Srmacklem struct nfscllock **other_lopp, int local) 2167191783Srmacklem{ 2168191783Srmacklem struct nfscllock *new_lop = *new_lopp; 2169191783Srmacklem struct nfscllock *lop, *tlop, *ilop; 2170191783Srmacklem struct nfscllock *other_lop; 2171191783Srmacklem int unlock = 0, modified = 0; 2172191783Srmacklem u_int64_t tmp; 2173191783Srmacklem 2174191783Srmacklem /* 2175191783Srmacklem * Work down the list until the lock is merged. 2176191783Srmacklem */ 2177191783Srmacklem if (new_lop->nfslo_type == F_UNLCK) 2178191783Srmacklem unlock = 1; 2179191783Srmacklem ilop = (struct nfscllock *)lp; 2180191783Srmacklem lop = LIST_FIRST(&lp->nfsl_lock); 2181191783Srmacklem while (lop != NULL) { 2182191783Srmacklem /* 2183191783Srmacklem * Only check locks for this file that aren't before the start of 2184191783Srmacklem * new lock's range. 2185191783Srmacklem */ 2186191783Srmacklem if (lop->nfslo_end >= new_lop->nfslo_first) { 2187191783Srmacklem if (new_lop->nfslo_end < lop->nfslo_first) { 2188191783Srmacklem /* 2189191783Srmacklem * If the new lock ends before the start of the 2190191783Srmacklem * current lock's range, no merge, just insert 2191191783Srmacklem * the new lock. 2192191783Srmacklem */ 2193191783Srmacklem break; 2194191783Srmacklem } 2195191783Srmacklem if (new_lop->nfslo_type == lop->nfslo_type || 2196191783Srmacklem (new_lop->nfslo_first <= lop->nfslo_first && 2197191783Srmacklem new_lop->nfslo_end >= lop->nfslo_end)) { 2198191783Srmacklem /* 2199191783Srmacklem * This lock can be absorbed by the new lock/unlock. 2200191783Srmacklem * This happens when it covers the entire range 2201191783Srmacklem * of the old lock or is contiguous 2202191783Srmacklem * with the old lock and is of the same type or an 2203191783Srmacklem * unlock. 2204191783Srmacklem */ 2205191783Srmacklem if (new_lop->nfslo_type != lop->nfslo_type || 2206191783Srmacklem new_lop->nfslo_first != lop->nfslo_first || 2207191783Srmacklem new_lop->nfslo_end != lop->nfslo_end) 2208191783Srmacklem modified = 1; 2209191783Srmacklem if (lop->nfslo_first < new_lop->nfslo_first) 2210191783Srmacklem new_lop->nfslo_first = lop->nfslo_first; 2211191783Srmacklem if (lop->nfslo_end > new_lop->nfslo_end) 2212191783Srmacklem new_lop->nfslo_end = lop->nfslo_end; 2213191783Srmacklem tlop = lop; 2214191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2215191783Srmacklem nfscl_freelock(tlop, local); 2216191783Srmacklem continue; 2217191783Srmacklem } 2218191783Srmacklem 2219191783Srmacklem /* 2220191783Srmacklem * All these cases are for contiguous locks that are not the 2221191783Srmacklem * same type, so they can't be merged. 2222191783Srmacklem */ 2223191783Srmacklem if (new_lop->nfslo_first <= lop->nfslo_first) { 2224191783Srmacklem /* 2225191783Srmacklem * This case is where the new lock overlaps with the 2226191783Srmacklem * first part of the old lock. Move the start of the 2227191783Srmacklem * old lock to just past the end of the new lock. The 2228191783Srmacklem * new lock will be inserted in front of the old, since 2229191783Srmacklem * ilop hasn't been updated. (We are done now.) 2230191783Srmacklem */ 2231191783Srmacklem if (lop->nfslo_first != new_lop->nfslo_end) { 2232191783Srmacklem lop->nfslo_first = new_lop->nfslo_end; 2233191783Srmacklem modified = 1; 2234191783Srmacklem } 2235191783Srmacklem break; 2236191783Srmacklem } 2237191783Srmacklem if (new_lop->nfslo_end >= lop->nfslo_end) { 2238191783Srmacklem /* 2239191783Srmacklem * This case is where the new lock overlaps with the 2240191783Srmacklem * end of the old lock's range. Move the old lock's 2241191783Srmacklem * end to just before the new lock's first and insert 2242191783Srmacklem * the new lock after the old lock. 2243191783Srmacklem * Might not be done yet, since the new lock could 2244191783Srmacklem * overlap further locks with higher ranges. 2245191783Srmacklem */ 2246191783Srmacklem if (lop->nfslo_end != new_lop->nfslo_first) { 2247191783Srmacklem lop->nfslo_end = new_lop->nfslo_first; 2248191783Srmacklem modified = 1; 2249191783Srmacklem } 2250191783Srmacklem ilop = lop; 2251191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2252191783Srmacklem continue; 2253191783Srmacklem } 2254191783Srmacklem /* 2255191783Srmacklem * The final case is where the new lock's range is in the 2256191783Srmacklem * middle of the current lock's and splits the current lock 2257191783Srmacklem * up. Use *other_lopp to handle the second part of the 2258191783Srmacklem * split old lock range. (We are done now.) 2259191783Srmacklem * For unlock, we use new_lop as other_lop and tmp, since 2260191783Srmacklem * other_lop and new_lop are the same for this case. 2261191783Srmacklem * We noted the unlock case above, so we don't need 2262191783Srmacklem * new_lop->nfslo_type any longer. 2263191783Srmacklem */ 2264191783Srmacklem tmp = new_lop->nfslo_first; 2265191783Srmacklem if (unlock) { 2266191783Srmacklem other_lop = new_lop; 2267191783Srmacklem *new_lopp = NULL; 2268191783Srmacklem } else { 2269191783Srmacklem other_lop = *other_lopp; 2270191783Srmacklem *other_lopp = NULL; 2271191783Srmacklem } 2272191783Srmacklem other_lop->nfslo_first = new_lop->nfslo_end; 2273191783Srmacklem other_lop->nfslo_end = lop->nfslo_end; 2274191783Srmacklem other_lop->nfslo_type = lop->nfslo_type; 2275191783Srmacklem lop->nfslo_end = tmp; 2276191783Srmacklem nfscl_insertlock(lp, other_lop, lop, local); 2277191783Srmacklem ilop = lop; 2278191783Srmacklem modified = 1; 2279191783Srmacklem break; 2280191783Srmacklem } 2281191783Srmacklem ilop = lop; 2282191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2283191783Srmacklem if (lop == NULL) 2284191783Srmacklem break; 2285191783Srmacklem } 2286191783Srmacklem 2287191783Srmacklem /* 2288191783Srmacklem * Insert the new lock in the list at the appropriate place. 2289191783Srmacklem */ 2290191783Srmacklem if (!unlock) { 2291191783Srmacklem nfscl_insertlock(lp, new_lop, ilop, local); 2292191783Srmacklem *new_lopp = NULL; 2293191783Srmacklem modified = 1; 2294191783Srmacklem } 2295191783Srmacklem return (modified); 2296191783Srmacklem} 2297191783Srmacklem 2298191783Srmacklem/* 2299191783Srmacklem * This function must be run as a kernel thread. 2300191783Srmacklem * It does Renew Ops and recovery, when required. 2301191783Srmacklem */ 2302191783SrmacklemAPPLESTATIC void 2303191783Srmacklemnfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p) 2304191783Srmacklem{ 2305191783Srmacklem struct nfsclowner *owp, *nowp; 2306191783Srmacklem struct nfsclopen *op; 2307191783Srmacklem struct nfscllockowner *lp, *nlp, *olp; 2308191783Srmacklem struct nfscldeleghead dh; 2309191783Srmacklem struct nfscllockownerhead lh; 2310191783Srmacklem struct nfscldeleg *dp, *ndp; 2311191783Srmacklem struct ucred *cred; 2312191783Srmacklem u_int32_t clidrev; 2313191783Srmacklem int error, cbpathdown, islept, igotlock, ret, clearok; 2314191783Srmacklem 2315191783Srmacklem cred = newnfs_getcred(); 2316191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD; 2317191783Srmacklem for(;;) { 2318191783Srmacklem newnfs_setroot(cred); 2319191783Srmacklem cbpathdown = 0; 2320191783Srmacklem if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) 2321191783Srmacklem nfscl_recover(clp, cred, p); 2322191783Srmacklem if (clp->nfsc_expire <= NFSD_MONOSEC && 2323191783Srmacklem (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) { 2324191783Srmacklem clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew; 2325191783Srmacklem clidrev = clp->nfsc_clientidrev; 2326191783Srmacklem error = nfsrpc_renew(clp, cred, p); 2327191783Srmacklem if (error == NFSERR_CBPATHDOWN) 2328191783Srmacklem cbpathdown = 1; 2329191783Srmacklem else if (error == NFSERR_STALECLIENTID) 2330191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECOVER; 2331191783Srmacklem else if (error == NFSERR_EXPIRED) 2332191783Srmacklem (void) nfscl_hasexpired(clp, clidrev, p); 2333191783Srmacklem } 2334191783Srmacklem 2335191783Srmacklem LIST_INIT(&lh); 2336191783Srmacklem TAILQ_INIT(&dh); 2337191783Srmacklem NFSLOCKCLSTATE(); 2338191783Srmacklem if (cbpathdown) 2339191783Srmacklem /* It's a Total Recall! */ 2340191783Srmacklem nfscl_totalrecall(clp); 2341191783Srmacklem 2342191783Srmacklem /* 2343191783Srmacklem * Now, handle defunct owners. 2344191783Srmacklem */ 2345191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 2346191783Srmacklem while (owp != NULL) { 2347191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 2348191783Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) { 2349191783Srmacklem if (owp->nfsow_defunct) 2350191783Srmacklem nfscl_freeopenowner(owp, 0); 2351191783Srmacklem } else { 2352191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2353191783Srmacklem lp = LIST_FIRST(&op->nfso_lock); 2354191783Srmacklem while (lp != NULL) { 2355191783Srmacklem nlp = LIST_NEXT(lp, nfsl_list); 2356191783Srmacklem if (lp->nfsl_defunct && 2357191783Srmacklem LIST_EMPTY(&lp->nfsl_lock)) { 2358191783Srmacklem LIST_FOREACH(olp, &lh, nfsl_list) { 2359191783Srmacklem if (!NFSBCMP(olp->nfsl_owner, 2360191783Srmacklem lp->nfsl_owner,NFSV4CL_LOCKNAMELEN)) 2361191783Srmacklem break; 2362191783Srmacklem } 2363191783Srmacklem if (olp == NULL) { 2364191783Srmacklem LIST_REMOVE(lp, nfsl_list); 2365191783Srmacklem LIST_INSERT_HEAD(&lh, lp, nfsl_list); 2366191783Srmacklem } else { 2367191783Srmacklem nfscl_freelockowner(lp, 0); 2368191783Srmacklem } 2369191783Srmacklem } 2370191783Srmacklem lp = nlp; 2371191783Srmacklem } 2372191783Srmacklem } 2373191783Srmacklem } 2374191783Srmacklem owp = nowp; 2375191783Srmacklem } 2376191783Srmacklem 2377191783Srmacklem /* also search the defunct list */ 2378191783Srmacklem lp = LIST_FIRST(&clp->nfsc_defunctlockowner); 2379191783Srmacklem while (lp != NULL) { 2380191783Srmacklem nlp = LIST_NEXT(lp, nfsl_list); 2381191783Srmacklem if (lp->nfsl_defunct) { 2382191783Srmacklem LIST_FOREACH(olp, &lh, nfsl_list) { 2383191783Srmacklem if (!NFSBCMP(olp->nfsl_owner, lp->nfsl_owner, 2384191783Srmacklem NFSV4CL_LOCKNAMELEN)) 2385191783Srmacklem break; 2386191783Srmacklem } 2387191783Srmacklem if (olp == NULL) { 2388191783Srmacklem LIST_REMOVE(lp, nfsl_list); 2389191783Srmacklem LIST_INSERT_HEAD(&lh, lp, nfsl_list); 2390191783Srmacklem } else { 2391191783Srmacklem nfscl_freelockowner(lp, 0); 2392191783Srmacklem } 2393191783Srmacklem } 2394191783Srmacklem lp = nlp; 2395191783Srmacklem } 2396191783Srmacklem /* and release defunct lock owners */ 2397191783Srmacklem LIST_FOREACH_SAFE(lp, &lh, nfsl_list, nlp) { 2398191783Srmacklem nfscl_freelockowner(lp, 0); 2399191783Srmacklem } 2400191783Srmacklem 2401191783Srmacklem /* 2402191783Srmacklem * Do the recall on any delegations. To avoid trouble, always 2403191783Srmacklem * come back up here after having slept. 2404191783Srmacklem */ 2405191783Srmacklem igotlock = 0; 2406191783Srmacklemtryagain: 2407191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 2408191783Srmacklem while (dp != NULL) { 2409191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 2410191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_RECALL)) { 2411191783Srmacklem /* 2412191783Srmacklem * Wait for outstanding I/O ops to be done. 2413191783Srmacklem */ 2414191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 2415191783Srmacklem if (igotlock) { 2416191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2417191783Srmacklem igotlock = 0; 2418191783Srmacklem } 2419191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= 2420191783Srmacklem NFSV4LOCK_WANTED; 2421191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 2422191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", 2423191783Srmacklem NULL); 2424191783Srmacklem goto tryagain; 2425191783Srmacklem } 2426191783Srmacklem while (!igotlock) { 2427191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 2428191783Srmacklem &islept, NFSCLSTATEMUTEXPTR); 2429191783Srmacklem if (islept) 2430191783Srmacklem goto tryagain; 2431191783Srmacklem } 2432191783Srmacklem NFSUNLOCKCLSTATE(); 2433191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 2434191783Srmacklem ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp, 2435191783Srmacklem NULL, cred, p); 2436191783Srmacklem if (!ret) { 2437191783Srmacklem nfscl_cleandeleg(dp); 2438191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, 2439191783Srmacklem nfsdl_list); 2440191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 2441191783Srmacklem TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list); 2442191783Srmacklem nfscl_delegcnt--; 2443191783Srmacklem newnfsstats.cldelegates--; 2444191783Srmacklem } 2445191783Srmacklem NFSLOCKCLSTATE(); 2446191783Srmacklem } 2447191783Srmacklem dp = ndp; 2448191783Srmacklem } 2449191783Srmacklem 2450191783Srmacklem /* 2451191783Srmacklem * Clear out old delegations, if we are above the high water 2452191783Srmacklem * mark. Only clear out ones with no state related to them. 2453191783Srmacklem * The tailq list is in LRU order. 2454191783Srmacklem */ 2455191783Srmacklem dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead); 2456191783Srmacklem while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) { 2457191783Srmacklem ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list); 2458191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt == 0 && 2459191783Srmacklem dp->nfsdl_rwlock.nfslock_lock == 0 && 2460191783Srmacklem dp->nfsdl_timestamp < NFSD_MONOSEC && 2461191783Srmacklem !(dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED | 2462191783Srmacklem NFSCLDL_NEEDRECLAIM))) { 2463191783Srmacklem clearok = 1; 2464191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 2465191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2466195819Srmacklem if (op != NULL) { 2467191783Srmacklem clearok = 0; 2468191783Srmacklem break; 2469191783Srmacklem } 2470191783Srmacklem } 2471191783Srmacklem if (clearok) { 2472191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 2473191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 2474191783Srmacklem clearok = 0; 2475191783Srmacklem break; 2476191783Srmacklem } 2477191783Srmacklem } 2478191783Srmacklem } 2479191783Srmacklem if (clearok) { 2480191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 2481191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 2482191783Srmacklem TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list); 2483191783Srmacklem nfscl_delegcnt--; 2484191783Srmacklem newnfsstats.cldelegates--; 2485191783Srmacklem } 2486191783Srmacklem } 2487191783Srmacklem dp = ndp; 2488191783Srmacklem } 2489191783Srmacklem if (igotlock) 2490191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2491191783Srmacklem NFSUNLOCKCLSTATE(); 2492191783Srmacklem 2493191783Srmacklem /* 2494191783Srmacklem * Delegreturn any delegations cleaned out or recalled. 2495191783Srmacklem */ 2496191783Srmacklem TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) { 2497191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 2498191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); 2499191783Srmacklem TAILQ_REMOVE(&dh, dp, nfsdl_list); 2500191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 2501191783Srmacklem } 2502191783Srmacklem 2503191783Srmacklem#if defined(APPLEKEXT) || defined(__FreeBSD__) 2504191783Srmacklem /* 2505191783Srmacklem * Simulate the calls to nfscl_cleanup() when a process 2506191783Srmacklem * exits, since the call can't be patched into exit(). 2507191783Srmacklem */ 2508191783Srmacklem { 2509191783Srmacklem struct timespec mytime; 2510191783Srmacklem static time_t prevsec = 0; 2511191783Srmacklem 2512191783Srmacklem NFSGETNANOTIME(&mytime); 2513191783Srmacklem if (prevsec != mytime.tv_sec) { 2514191783Srmacklem prevsec = mytime.tv_sec; 2515191783Srmacklem nfscl_cleanupkext(clp); 2516191783Srmacklem } 2517191783Srmacklem } 2518191783Srmacklem#endif /* APPLEKEXT || __FreeBSD__ */ 2519191783Srmacklem 2520191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0) 2521191783Srmacklem (void) tsleep((caddr_t)clp, PWAIT, "nfscl", hz); 2522191783Srmacklem if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) { 2523191783Srmacklem NFSFREECRED(cred); 2524191783Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD; 2525191783Srmacklem wakeup((caddr_t)clp); 2526191783Srmacklem return; 2527191783Srmacklem } 2528191783Srmacklem } 2529191783Srmacklem} 2530191783Srmacklem 2531191783Srmacklem/* 2532191783Srmacklem * Initiate state recovery. Called when NFSERR_STALECLIENTID or 2533191783Srmacklem * NFSERR_STALESTATEID is received. 2534191783Srmacklem */ 2535191783SrmacklemAPPLESTATIC void 2536191783Srmacklemnfscl_initiate_recovery(struct nfsclclient *clp) 2537191783Srmacklem{ 2538191783Srmacklem 2539191783Srmacklem if (clp == NULL) 2540191783Srmacklem return; 2541191783Srmacklem NFSLOCKCLSTATE(); 2542191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECOVER; 2543191783Srmacklem NFSUNLOCKCLSTATE(); 2544191783Srmacklem wakeup((caddr_t)clp); 2545191783Srmacklem} 2546191783Srmacklem 2547191783Srmacklem/* 2548191783Srmacklem * Dump out the state stuff for debugging. 2549191783Srmacklem */ 2550191783SrmacklemAPPLESTATIC void 2551191783Srmacklemnfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens, 2552191783Srmacklem int lockowner, int locks) 2553191783Srmacklem{ 2554191783Srmacklem struct nfsclclient *clp; 2555191783Srmacklem struct nfsclowner *owp; 2556191783Srmacklem struct nfsclopen *op; 2557191783Srmacklem struct nfscllockowner *lp; 2558191783Srmacklem struct nfscllock *lop; 2559191783Srmacklem struct nfscldeleg *dp; 2560191783Srmacklem 2561191783Srmacklem clp = nmp->nm_clp; 2562191783Srmacklem if (clp == NULL) { 2563191783Srmacklem printf("nfscl dumpstate NULL clp\n"); 2564191783Srmacklem return; 2565191783Srmacklem } 2566191783Srmacklem NFSLOCKCLSTATE(); 2567191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 2568191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 2569191783Srmacklem if (openowner && !LIST_EMPTY(&owp->nfsow_open)) 2570191783Srmacklem printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n", 2571191783Srmacklem owp->nfsow_owner[0], owp->nfsow_owner[1], 2572191783Srmacklem owp->nfsow_owner[2], owp->nfsow_owner[3], 2573191783Srmacklem owp->nfsow_seqid); 2574191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2575191783Srmacklem if (opens) 2576191783Srmacklem printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n", 2577191783Srmacklem op->nfso_stateid.other[0], op->nfso_stateid.other[1], 2578191783Srmacklem op->nfso_stateid.other[2], op->nfso_opencnt, 2579191783Srmacklem op->nfso_fh[12]); 2580191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 2581191783Srmacklem if (lockowner) 2582191783Srmacklem printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n", 2583191783Srmacklem lp->nfsl_owner[0], lp->nfsl_owner[1], 2584191783Srmacklem lp->nfsl_owner[2], lp->nfsl_owner[3], 2585191783Srmacklem lp->nfsl_seqid, 2586191783Srmacklem lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1], 2587191783Srmacklem lp->nfsl_stateid.other[2]); 2588191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 2589191783Srmacklem if (locks) 2590191783Srmacklem#ifdef __FreeBSD__ 2591191783Srmacklem printf("lck typ=%d fst=%ju end=%ju\n", 2592191783Srmacklem lop->nfslo_type, (intmax_t)lop->nfslo_first, 2593191783Srmacklem (intmax_t)lop->nfslo_end); 2594191783Srmacklem#else 2595191783Srmacklem printf("lck typ=%d fst=%qd end=%qd\n", 2596191783Srmacklem lop->nfslo_type, lop->nfslo_first, 2597191783Srmacklem lop->nfslo_end); 2598191783Srmacklem#endif 2599191783Srmacklem } 2600191783Srmacklem } 2601191783Srmacklem } 2602191783Srmacklem } 2603191783Srmacklem } 2604191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2605191783Srmacklem if (openowner && !LIST_EMPTY(&owp->nfsow_open)) 2606191783Srmacklem printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n", 2607191783Srmacklem owp->nfsow_owner[0], owp->nfsow_owner[1], 2608191783Srmacklem owp->nfsow_owner[2], owp->nfsow_owner[3], 2609191783Srmacklem owp->nfsow_seqid); 2610191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2611191783Srmacklem if (opens) 2612191783Srmacklem printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n", 2613191783Srmacklem op->nfso_stateid.other[0], op->nfso_stateid.other[1], 2614191783Srmacklem op->nfso_stateid.other[2], op->nfso_opencnt, 2615191783Srmacklem op->nfso_fh[12]); 2616191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 2617191783Srmacklem if (lockowner) 2618191783Srmacklem printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n", 2619191783Srmacklem lp->nfsl_owner[0], lp->nfsl_owner[1], 2620191783Srmacklem lp->nfsl_owner[2], lp->nfsl_owner[3], 2621191783Srmacklem lp->nfsl_seqid, 2622191783Srmacklem lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1], 2623191783Srmacklem lp->nfsl_stateid.other[2]); 2624191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 2625191783Srmacklem if (locks) 2626191783Srmacklem#ifdef __FreeBSD__ 2627191783Srmacklem printf("lck typ=%d fst=%ju end=%ju\n", 2628191783Srmacklem lop->nfslo_type, (intmax_t)lop->nfslo_first, 2629191783Srmacklem (intmax_t)lop->nfslo_end); 2630191783Srmacklem#else 2631191783Srmacklem printf("lck typ=%d fst=%qd end=%qd\n", 2632191783Srmacklem lop->nfslo_type, lop->nfslo_first, 2633191783Srmacklem lop->nfslo_end); 2634191783Srmacklem#endif 2635191783Srmacklem } 2636191783Srmacklem } 2637191783Srmacklem } 2638191783Srmacklem } 2639191783Srmacklem NFSUNLOCKCLSTATE(); 2640191783Srmacklem} 2641191783Srmacklem 2642191783Srmacklem/* 2643191783Srmacklem * Check for duplicate open owners and opens. 2644191783Srmacklem * (Only used as a diagnostic aid.) 2645191783Srmacklem */ 2646191783SrmacklemAPPLESTATIC void 2647191783Srmacklemnfscl_dupopen(vnode_t vp, int dupopens) 2648191783Srmacklem{ 2649191783Srmacklem struct nfsclclient *clp; 2650191783Srmacklem struct nfsclowner *owp, *owp2; 2651191783Srmacklem struct nfsclopen *op, *op2; 2652191783Srmacklem struct nfsfh *nfhp; 2653191783Srmacklem 2654191783Srmacklem clp = VFSTONFS(vnode_mount(vp))->nm_clp; 2655191783Srmacklem if (clp == NULL) { 2656191783Srmacklem printf("nfscl dupopen NULL clp\n"); 2657191783Srmacklem return; 2658191783Srmacklem } 2659191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 2660191783Srmacklem NFSLOCKCLSTATE(); 2661191783Srmacklem 2662191783Srmacklem /* 2663191783Srmacklem * First, search for duplicate owners. 2664191783Srmacklem * These should never happen! 2665191783Srmacklem */ 2666191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2667191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2668191783Srmacklem if (owp != owp2 && 2669191783Srmacklem !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner, 2670191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 2671191783Srmacklem NFSUNLOCKCLSTATE(); 2672191783Srmacklem printf("DUP OWNER\n"); 2673191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0); 2674191783Srmacklem return; 2675191783Srmacklem } 2676191783Srmacklem } 2677191783Srmacklem } 2678191783Srmacklem 2679191783Srmacklem /* 2680191783Srmacklem * Now, search for duplicate stateids. 2681191783Srmacklem * These shouldn't happen, either. 2682191783Srmacklem */ 2683191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2684191783Srmacklem LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) { 2685191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2686191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2687191783Srmacklem if (op != op2 && 2688191783Srmacklem (op->nfso_stateid.other[0] != 0 || 2689191783Srmacklem op->nfso_stateid.other[1] != 0 || 2690191783Srmacklem op->nfso_stateid.other[2] != 0) && 2691191783Srmacklem op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] && 2692191783Srmacklem op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] && 2693191783Srmacklem op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) { 2694191783Srmacklem NFSUNLOCKCLSTATE(); 2695191783Srmacklem printf("DUP STATEID\n"); 2696191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 2697191783Srmacklem 0); 2698191783Srmacklem return; 2699191783Srmacklem } 2700191783Srmacklem } 2701191783Srmacklem } 2702191783Srmacklem } 2703191783Srmacklem } 2704191783Srmacklem 2705191783Srmacklem /* 2706191783Srmacklem * Now search for duplicate opens. 2707191783Srmacklem * Duplicate opens for the same owner 2708191783Srmacklem * should never occur. Other duplicates are 2709191783Srmacklem * possible and are checked for if "dupopens" 2710191783Srmacklem * is true. 2711191783Srmacklem */ 2712191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2713191783Srmacklem LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) { 2714191783Srmacklem if (nfhp->nfh_len == op2->nfso_fhlen && 2715191783Srmacklem !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) { 2716191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2717191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2718191783Srmacklem if (op != op2 && nfhp->nfh_len == op->nfso_fhlen && 2719191783Srmacklem !NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) && 2720191783Srmacklem (!NFSBCMP(op->nfso_own->nfsow_owner, 2721191783Srmacklem op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) || 2722191783Srmacklem dupopens)) { 2723191783Srmacklem if (!NFSBCMP(op->nfso_own->nfsow_owner, 2724191783Srmacklem op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) { 2725191783Srmacklem NFSUNLOCKCLSTATE(); 2726191783Srmacklem printf("BADDUP OPEN\n"); 2727191783Srmacklem } else { 2728191783Srmacklem NFSUNLOCKCLSTATE(); 2729191783Srmacklem printf("DUP OPEN\n"); 2730191783Srmacklem } 2731191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 2732191783Srmacklem 0, 0); 2733191783Srmacklem return; 2734191783Srmacklem } 2735191783Srmacklem } 2736191783Srmacklem } 2737191783Srmacklem } 2738191783Srmacklem } 2739191783Srmacklem } 2740191783Srmacklem NFSUNLOCKCLSTATE(); 2741191783Srmacklem} 2742191783Srmacklem 2743191783Srmacklem/* 2744191783Srmacklem * During close, find an open that needs to be dereferenced and 2745191783Srmacklem * dereference it. If there are no more opens for this file, 2746195510Srmacklem * log a message to that effect. 2747195510Srmacklem * Opens aren't actually Close'd until VOP_INACTIVE() is performed 2748195510Srmacklem * on the file's vnode. 2749191783Srmacklem * This is the safe way, since it is difficult to identify 2750195510Srmacklem * which open the close is for and I/O can be performed after the 2751195510Srmacklem * close(2) system call when a file is mmap'd. 2752191783Srmacklem * If it returns 0 for success, there will be a referenced 2753195510Srmacklem * clp returned via clpp. 2754191783Srmacklem */ 2755191783SrmacklemAPPLESTATIC int 2756195510Srmacklemnfscl_getclose(vnode_t vp, struct nfsclclient **clpp) 2757191783Srmacklem{ 2758191783Srmacklem struct nfsclclient *clp; 2759195510Srmacklem struct nfsclowner *owp; 2760195510Srmacklem struct nfsclopen *op; 2761191783Srmacklem struct nfscldeleg *dp; 2762191783Srmacklem struct nfsfh *nfhp; 2763195510Srmacklem int error, notdecr; 2764191783Srmacklem 2765192337Srmacklem error = nfscl_getcl(vp, NULL, NULL, &clp); 2766191783Srmacklem if (error) 2767191783Srmacklem return (error); 2768191783Srmacklem *clpp = clp; 2769191783Srmacklem 2770191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 2771191783Srmacklem notdecr = 1; 2772191783Srmacklem NFSLOCKCLSTATE(); 2773191783Srmacklem /* 2774191783Srmacklem * First, look for one under a delegation that was locally issued 2775191783Srmacklem * and just decrement the opencnt for it. Since all my Opens against 2776191783Srmacklem * the server are DENY_NONE, I don't see a problem with hanging 2777191783Srmacklem * onto them. (It is much easier to use one of the extant Opens 2778191783Srmacklem * that I already have on the server when a Delegation is recalled 2779195510Srmacklem * than to do fresh Opens.) Someday, I might need to rethink this, but. 2780191783Srmacklem */ 2781191783Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len); 2782191783Srmacklem if (dp != NULL) { 2783191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 2784191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2785191783Srmacklem if (op != NULL) { 2786191783Srmacklem /* 2787191783Srmacklem * Since a delegation is for a file, there 2788191783Srmacklem * should never be more than one open for 2789191783Srmacklem * each openowner. 2790191783Srmacklem */ 2791191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 2792191783Srmacklem panic("nfscdeleg opens"); 2793191783Srmacklem if (notdecr && op->nfso_opencnt > 0) { 2794191783Srmacklem notdecr = 0; 2795191783Srmacklem op->nfso_opencnt--; 2796191783Srmacklem break; 2797191783Srmacklem } 2798191783Srmacklem } 2799191783Srmacklem } 2800191783Srmacklem } 2801191783Srmacklem 2802191783Srmacklem /* Now process the opens against the server. */ 2803191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2804195510Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2805192337Srmacklem if (op->nfso_fhlen == nfhp->nfh_len && 2806192337Srmacklem !NFSBCMP(op->nfso_fh, nfhp->nfh_fh, 2807192337Srmacklem nfhp->nfh_len)) { 2808192337Srmacklem /* Found an open, decrement cnt if possible */ 2809192337Srmacklem if (notdecr && op->nfso_opencnt > 0) { 2810192337Srmacklem notdecr = 0; 2811192337Srmacklem op->nfso_opencnt--; 2812192337Srmacklem } 2813192337Srmacklem /* 2814195510Srmacklem * There are more opens, so just return. 2815192337Srmacklem */ 2816192337Srmacklem if (op->nfso_opencnt > 0) { 2817192337Srmacklem NFSUNLOCKCLSTATE(); 2818192337Srmacklem return (0); 2819192337Srmacklem } 2820191783Srmacklem } 2821191783Srmacklem } 2822191783Srmacklem } 2823195510Srmacklem NFSUNLOCKCLSTATE(); 2824195510Srmacklem if (notdecr) 2825195510Srmacklem printf("nfscl: never fnd open\n"); 2826195510Srmacklem return (0); 2827195510Srmacklem} 2828191783Srmacklem 2829195510SrmacklemAPPLESTATIC int 2830195510Srmacklemnfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p) 2831195510Srmacklem{ 2832195510Srmacklem struct nfsclclient *clp; 2833195510Srmacklem struct nfsclowner *owp, *nowp; 2834195510Srmacklem struct nfsclopen *op; 2835195510Srmacklem struct nfscldeleg *dp; 2836195510Srmacklem struct nfsfh *nfhp; 2837195510Srmacklem int error; 2838195510Srmacklem 2839195510Srmacklem error = nfscl_getcl(vp, NULL, NULL, &clp); 2840195510Srmacklem if (error) 2841195510Srmacklem return (error); 2842195510Srmacklem *clpp = clp; 2843195510Srmacklem 2844195510Srmacklem nfhp = VTONFS(vp)->n_fhp; 2845195510Srmacklem NFSLOCKCLSTATE(); 2846195510Srmacklem /* 2847195510Srmacklem * First get rid of the local Open structures, which should be no 2848195510Srmacklem * longer in use. 2849195510Srmacklem */ 2850195510Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len); 2851195510Srmacklem if (dp != NULL) { 2852195510Srmacklem LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) { 2853191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2854195510Srmacklem if (op != NULL) { 2855195510Srmacklem KASSERT((op->nfso_opencnt == 0), 2856195510Srmacklem ("nfscl: bad open cnt on deleg")); 2857195510Srmacklem nfscl_freeopen(op, 1); 2858191783Srmacklem } 2859195510Srmacklem nfscl_freeopenowner(owp, 1); 2860191783Srmacklem } 2861195510Srmacklem } 2862195510Srmacklem 2863195510Srmacklem /* Now process the opens against the server. */ 2864195510Srmacklemlookformore: 2865195510Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2866195510Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2867195510Srmacklem while (op != NULL) { 2868195510Srmacklem if (op->nfso_fhlen == nfhp->nfh_len && 2869195510Srmacklem !NFSBCMP(op->nfso_fh, nfhp->nfh_fh, 2870195510Srmacklem nfhp->nfh_len)) { 2871195510Srmacklem /* Found an open, close it. */ 2872195510Srmacklem KASSERT((op->nfso_opencnt == 0), 2873195510Srmacklem ("nfscl: bad open cnt on server")); 2874195510Srmacklem NFSUNLOCKCLSTATE(); 2875195510Srmacklem nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op, 2876195510Srmacklem p); 2877195510Srmacklem NFSLOCKCLSTATE(); 2878195510Srmacklem goto lookformore; 2879191783Srmacklem } 2880195510Srmacklem op = LIST_NEXT(op, nfso_list); 2881191783Srmacklem } 2882191783Srmacklem } 2883191783Srmacklem NFSUNLOCKCLSTATE(); 2884191783Srmacklem return (0); 2885191783Srmacklem} 2886191783Srmacklem 2887191783Srmacklem/* 2888191783Srmacklem * Return all delegations on this client. 2889191783Srmacklem * (Must be called with client sleep lock.) 2890191783Srmacklem */ 2891191783Srmacklemstatic void 2892191783Srmacklemnfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p) 2893191783Srmacklem{ 2894191783Srmacklem struct nfscldeleg *dp, *ndp; 2895191783Srmacklem struct ucred *cred; 2896191783Srmacklem 2897191783Srmacklem cred = newnfs_getcred(); 2898191783Srmacklem TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) { 2899191783Srmacklem nfscl_cleandeleg(dp); 2900191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); 2901191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 2902191783Srmacklem } 2903191783Srmacklem NFSFREECRED(cred); 2904191783Srmacklem} 2905191783Srmacklem 2906191783Srmacklem/* 2907191783Srmacklem * Do a callback RPC. 2908191783Srmacklem */ 2909191783SrmacklemAPPLESTATIC void 2910191783Srmacklemnfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) 2911191783Srmacklem{ 2912191783Srmacklem int i, op; 2913191783Srmacklem u_int32_t *tl; 2914191783Srmacklem struct nfsclclient *clp; 2915191783Srmacklem struct nfscldeleg *dp = NULL; 2916191783Srmacklem int numops, taglen = -1, error = 0, trunc, ret = 0; 2917191783Srmacklem u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident; 2918191783Srmacklem u_char tag[NFSV4_SMALLSTR + 1], *tagstr; 2919191783Srmacklem vnode_t vp = NULL; 2920191783Srmacklem struct nfsnode *np; 2921191783Srmacklem struct vattr va; 2922191783Srmacklem struct nfsfh *nfhp; 2923191783Srmacklem mount_t mp; 2924191783Srmacklem nfsattrbit_t attrbits, rattrbits; 2925191783Srmacklem nfsv4stateid_t stateid; 2926191783Srmacklem 2927191783Srmacklem nfsrvd_rephead(nd); 2928191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 2929191783Srmacklem taglen = fxdr_unsigned(int, *tl); 2930191783Srmacklem if (taglen < 0) { 2931191783Srmacklem error = EBADRPC; 2932191783Srmacklem goto nfsmout; 2933191783Srmacklem } 2934191783Srmacklem if (taglen <= NFSV4_SMALLSTR) 2935191783Srmacklem tagstr = tag; 2936191783Srmacklem else 2937191783Srmacklem tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK); 2938191783Srmacklem error = nfsrv_mtostr(nd, tagstr, taglen); 2939191783Srmacklem if (error) { 2940191783Srmacklem if (taglen > NFSV4_SMALLSTR) 2941191783Srmacklem free(tagstr, M_TEMP); 2942191783Srmacklem taglen = -1; 2943191783Srmacklem goto nfsmout; 2944191783Srmacklem } 2945191783Srmacklem (void) nfsm_strtom(nd, tag, taglen); 2946191783Srmacklem if (taglen > NFSV4_SMALLSTR) { 2947191783Srmacklem free(tagstr, M_TEMP); 2948191783Srmacklem } 2949191783Srmacklem NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED); 2950191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 2951191783Srmacklem minorvers = fxdr_unsigned(u_int32_t, *tl++); 2952191783Srmacklem if (minorvers != NFSV4_MINORVERSION) 2953191783Srmacklem nd->nd_repstat = NFSERR_MINORVERMISMATCH; 2954191783Srmacklem cbident = fxdr_unsigned(u_int32_t, *tl++); 2955191783Srmacklem if (nd->nd_repstat) 2956191783Srmacklem numops = 0; 2957191783Srmacklem else 2958191783Srmacklem numops = fxdr_unsigned(int, *tl); 2959191783Srmacklem /* 2960191783Srmacklem * Loop around doing the sub ops. 2961191783Srmacklem */ 2962191783Srmacklem for (i = 0; i < numops; i++) { 2963191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 2964191783Srmacklem NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED); 2965191783Srmacklem *repp++ = *tl; 2966191783Srmacklem op = fxdr_unsigned(int, *tl); 2967191783Srmacklem if (op < NFSV4OP_CBGETATTR || op > NFSV4OP_CBRECALL) { 2968191783Srmacklem nd->nd_repstat = NFSERR_OPILLEGAL; 2969191783Srmacklem *repp = nfscl_errmap(nd); 2970191783Srmacklem retops++; 2971191783Srmacklem break; 2972191783Srmacklem } 2973191783Srmacklem nd->nd_procnum = op; 2974191783Srmacklem newnfsstats.cbrpccnt[nd->nd_procnum]++; 2975191783Srmacklem switch (op) { 2976191783Srmacklem case NFSV4OP_CBGETATTR: 2977191783Srmacklem clp = NULL; 2978191783Srmacklem error = nfsm_getfh(nd, &nfhp); 2979191783Srmacklem if (!error) 2980191783Srmacklem error = nfsrv_getattrbits(nd, &attrbits, 2981191783Srmacklem NULL, NULL); 2982191783Srmacklem if (!error) { 2983191783Srmacklem mp = nfscl_getmnt(cbident); 2984191783Srmacklem if (mp == NULL) 2985191783Srmacklem error = NFSERR_SERVERFAULT; 2986191783Srmacklem } 2987191783Srmacklem if (!error) { 2988191783Srmacklem dp = NULL; 2989191783Srmacklem NFSLOCKCLSTATE(); 2990191783Srmacklem clp = nfscl_findcl(VFSTONFS(mp)); 2991191783Srmacklem if (clp != NULL) 2992191783Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, 2993191783Srmacklem nfhp->nfh_len); 2994191783Srmacklem NFSUNLOCKCLSTATE(); 2995191783Srmacklem if (dp == NULL) 2996191783Srmacklem error = NFSERR_SERVERFAULT; 2997191783Srmacklem } 2998191783Srmacklem if (!error) { 2999191783Srmacklem ret = nfscl_ngetreopen(mp, nfhp->nfh_fh, 3000191783Srmacklem nfhp->nfh_len, p, &np); 3001191783Srmacklem if (!ret) 3002191783Srmacklem vp = NFSTOV(np); 3003191783Srmacklem } 3004191783Srmacklem if (nfhp != NULL) 3005191783Srmacklem FREE((caddr_t)nfhp, M_NFSFH); 3006191783Srmacklem if (!error) { 3007191783Srmacklem NFSZERO_ATTRBIT(&rattrbits); 3008191783Srmacklem if (NFSISSET_ATTRBIT(&attrbits, 3009191783Srmacklem NFSATTRBIT_SIZE)) { 3010191783Srmacklem if (!ret) 3011191783Srmacklem va.va_size = np->n_size; 3012191783Srmacklem else 3013191783Srmacklem va.va_size = dp->nfsdl_size; 3014191783Srmacklem NFSSETBIT_ATTRBIT(&rattrbits, 3015191783Srmacklem NFSATTRBIT_SIZE); 3016191783Srmacklem } 3017191783Srmacklem if (NFSISSET_ATTRBIT(&attrbits, 3018191783Srmacklem NFSATTRBIT_CHANGE)) { 3019191783Srmacklem va.va_filerev = dp->nfsdl_change; 3020191783Srmacklem if (ret || (np->n_flag & NDELEGMOD)) 3021191783Srmacklem va.va_filerev++; 3022191783Srmacklem NFSSETBIT_ATTRBIT(&rattrbits, 3023191783Srmacklem NFSATTRBIT_CHANGE); 3024191783Srmacklem } 3025191783Srmacklem (void) nfsv4_fillattr(nd, NULL, NULL, &va, 3026191783Srmacklem NULL, 0, &rattrbits, NULL, NULL, 0, 0); 3027191783Srmacklem if (!ret) 3028191783Srmacklem vrele(vp); 3029191783Srmacklem } 3030191783Srmacklem break; 3031191783Srmacklem case NFSV4OP_CBRECALL: 3032191783Srmacklem clp = NULL; 3033191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3034191783Srmacklem NFSX_UNSIGNED); 3035191783Srmacklem stateid.seqid = *tl++; 3036191783Srmacklem NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, 3037191783Srmacklem NFSX_STATEIDOTHER); 3038191783Srmacklem tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 3039191783Srmacklem trunc = fxdr_unsigned(int, *tl); 3040191783Srmacklem error = nfsm_getfh(nd, &nfhp); 3041191783Srmacklem if (!error) { 3042191783Srmacklem mp = nfscl_getmnt(cbident); 3043191783Srmacklem if (mp == NULL) 3044191783Srmacklem error = NFSERR_SERVERFAULT; 3045191783Srmacklem } 3046191783Srmacklem if (!error) { 3047191783Srmacklem NFSLOCKCLSTATE(); 3048191783Srmacklem clp = nfscl_findcl(VFSTONFS(mp)); 3049191783Srmacklem if (clp != NULL) { 3050191783Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, 3051191783Srmacklem nfhp->nfh_len); 3052191783Srmacklem if (dp != NULL) { 3053191783Srmacklem dp->nfsdl_flags |= 3054191783Srmacklem NFSCLDL_RECALL; 3055191783Srmacklem wakeup((caddr_t)clp); 3056191783Srmacklem } 3057191783Srmacklem } else { 3058191783Srmacklem error = NFSERR_SERVERFAULT; 3059191783Srmacklem } 3060191783Srmacklem NFSUNLOCKCLSTATE(); 3061191783Srmacklem } 3062191783Srmacklem if (nfhp != NULL) 3063191783Srmacklem FREE((caddr_t)nfhp, M_NFSFH); 3064191783Srmacklem break; 3065191783Srmacklem }; 3066191783Srmacklem if (error) { 3067191783Srmacklem if (error == EBADRPC || error == NFSERR_BADXDR) { 3068191783Srmacklem nd->nd_repstat = NFSERR_BADXDR; 3069191783Srmacklem } else { 3070191783Srmacklem nd->nd_repstat = error; 3071191783Srmacklem } 3072191783Srmacklem error = 0; 3073191783Srmacklem } 3074191783Srmacklem retops++; 3075191783Srmacklem if (nd->nd_repstat) { 3076191783Srmacklem *repp = nfscl_errmap(nd); 3077191783Srmacklem break; 3078191783Srmacklem } else 3079191783Srmacklem *repp = 0; /* NFS4_OK */ 3080191783Srmacklem } 3081191783Srmacklemnfsmout: 3082191783Srmacklem if (error) { 3083191783Srmacklem if (error == EBADRPC || error == NFSERR_BADXDR) 3084191783Srmacklem nd->nd_repstat = NFSERR_BADXDR; 3085191783Srmacklem else 3086191783Srmacklem printf("nfsv4 comperr1=%d\n", error); 3087191783Srmacklem } 3088191783Srmacklem if (taglen == -1) { 3089191783Srmacklem NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 3090191783Srmacklem *tl++ = 0; 3091191783Srmacklem *tl = 0; 3092191783Srmacklem } else { 3093191783Srmacklem *retopsp = txdr_unsigned(retops); 3094191783Srmacklem } 3095191783Srmacklem *nd->nd_errp = nfscl_errmap(nd); 3096191783Srmacklem} 3097191783Srmacklem 3098191783Srmacklem/* 3099191783Srmacklem * Generate the next cbident value. Basically just increment a static value 3100191783Srmacklem * and then check that it isn't already in the list, if it has wrapped around. 3101191783Srmacklem */ 3102191783Srmacklemstatic u_int32_t 3103191783Srmacklemnfscl_nextcbident(void) 3104191783Srmacklem{ 3105191783Srmacklem struct nfsclclient *clp; 3106191783Srmacklem int matched; 3107191783Srmacklem static u_int32_t nextcbident = 0; 3108191783Srmacklem static int haswrapped = 0; 3109191783Srmacklem 3110191783Srmacklem nextcbident++; 3111191783Srmacklem if (nextcbident == 0) 3112191783Srmacklem haswrapped = 1; 3113191783Srmacklem if (haswrapped) { 3114191783Srmacklem /* 3115191783Srmacklem * Search the clientid list for one already using this cbident. 3116191783Srmacklem */ 3117191783Srmacklem do { 3118191783Srmacklem matched = 0; 3119191783Srmacklem NFSLOCKCLSTATE(); 3120191783Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) { 3121191783Srmacklem if (clp->nfsc_cbident == nextcbident) { 3122191783Srmacklem matched = 1; 3123191783Srmacklem break; 3124191783Srmacklem } 3125191783Srmacklem } 3126191783Srmacklem NFSUNLOCKCLSTATE(); 3127191783Srmacklem if (matched == 1) 3128191783Srmacklem nextcbident++; 3129191783Srmacklem } while (matched); 3130191783Srmacklem } 3131191783Srmacklem return (nextcbident); 3132191783Srmacklem} 3133191783Srmacklem 3134191783Srmacklem/* 3135191783Srmacklem * Get the mount point related to a given cbident. 3136191783Srmacklem */ 3137191783Srmacklemstatic mount_t 3138191783Srmacklemnfscl_getmnt(u_int32_t cbident) 3139191783Srmacklem{ 3140191783Srmacklem struct nfsclclient *clp; 3141191783Srmacklem struct nfsmount *nmp; 3142191783Srmacklem 3143191783Srmacklem NFSLOCKCLSTATE(); 3144191783Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) { 3145191783Srmacklem if (clp->nfsc_cbident == cbident) 3146191783Srmacklem break; 3147191783Srmacklem } 3148191783Srmacklem if (clp == NULL) { 3149191783Srmacklem NFSUNLOCKCLSTATE(); 3150191783Srmacklem return (NULL); 3151191783Srmacklem } 3152191783Srmacklem nmp = clp->nfsc_nmp; 3153191783Srmacklem NFSUNLOCKCLSTATE(); 3154191783Srmacklem return (nmp->nm_mountp); 3155191783Srmacklem} 3156191783Srmacklem 3157191783Srmacklem/* 3158191783Srmacklem * Search for a lock conflict locally on the client. A conflict occurs if 3159191783Srmacklem * - not same owner and overlapping byte range and at least one of them is 3160191783Srmacklem * a write lock or this is an unlock. 3161191783Srmacklem */ 3162191783Srmacklemstatic int 3163201439Srmacklemnfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen, 3164201439Srmacklem struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp, 3165201439Srmacklem struct nfscllock **lopp) 3166191783Srmacklem{ 3167191783Srmacklem struct nfsclowner *owp; 3168191783Srmacklem struct nfsclopen *op; 3169191783Srmacklem int ret; 3170191783Srmacklem 3171191783Srmacklem if (dp != NULL) { 3172191783Srmacklem ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp); 3173191783Srmacklem if (ret) 3174191783Srmacklem return (ret); 3175191783Srmacklem } 3176191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 3177191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 3178201439Srmacklem if (op->nfso_fhlen == fhlen && 3179201439Srmacklem !NFSBCMP(op->nfso_fh, fhp, fhlen)) { 3180201439Srmacklem ret = nfscl_checkconflict(&op->nfso_lock, nlop, 3181201439Srmacklem own, lopp); 3182201439Srmacklem if (ret) 3183201439Srmacklem return (ret); 3184201439Srmacklem } 3185191783Srmacklem } 3186191783Srmacklem } 3187191783Srmacklem return (0); 3188191783Srmacklem} 3189191783Srmacklem 3190191783Srmacklemstatic int 3191191783Srmacklemnfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop, 3192191783Srmacklem u_int8_t *own, struct nfscllock **lopp) 3193191783Srmacklem{ 3194191783Srmacklem struct nfscllockowner *lp; 3195191783Srmacklem struct nfscllock *lop; 3196191783Srmacklem 3197191783Srmacklem LIST_FOREACH(lp, lhp, nfsl_list) { 3198191783Srmacklem if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) { 3199191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 3200191783Srmacklem if (lop->nfslo_first >= nlop->nfslo_end) 3201191783Srmacklem break; 3202191783Srmacklem if (lop->nfslo_end <= nlop->nfslo_first) 3203191783Srmacklem continue; 3204191783Srmacklem if (lop->nfslo_type == F_WRLCK || 3205191783Srmacklem nlop->nfslo_type == F_WRLCK || 3206191783Srmacklem nlop->nfslo_type == F_UNLCK) { 3207191783Srmacklem if (lopp != NULL) 3208191783Srmacklem *lopp = lop; 3209191783Srmacklem return (NFSERR_DENIED); 3210191783Srmacklem } 3211191783Srmacklem } 3212191783Srmacklem } 3213191783Srmacklem } 3214191783Srmacklem return (0); 3215191783Srmacklem} 3216191783Srmacklem 3217191783Srmacklem/* 3218191783Srmacklem * Check for a local conflicting lock. 3219191783Srmacklem */ 3220191783SrmacklemAPPLESTATIC int 3221191783Srmacklemnfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off, 3222191783Srmacklem u_int64_t len, struct flock *fl, NFSPROC_T *p) 3223191783Srmacklem{ 3224191783Srmacklem struct nfscllock *lop, nlck; 3225191783Srmacklem struct nfscldeleg *dp; 3226191783Srmacklem struct nfsnode *np; 3227191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 3228191783Srmacklem int error; 3229191783Srmacklem 3230191783Srmacklem nlck.nfslo_type = fl->l_type; 3231191783Srmacklem nlck.nfslo_first = off; 3232191783Srmacklem if (len == NFS64BITSSET) { 3233191783Srmacklem nlck.nfslo_end = NFS64BITSSET; 3234191783Srmacklem } else { 3235191783Srmacklem nlck.nfslo_end = off + len; 3236191783Srmacklem if (nlck.nfslo_end <= nlck.nfslo_first) 3237191783Srmacklem return (NFSERR_INVAL); 3238191783Srmacklem } 3239191783Srmacklem np = VTONFS(vp); 3240191783Srmacklem nfscl_filllockowner(p, own); 3241191783Srmacklem NFSLOCKCLSTATE(); 3242191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 3243201439Srmacklem error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 3244201439Srmacklem &nlck, own, dp, &lop); 3245201439Srmacklem if (error != 0) { 3246191783Srmacklem fl->l_whence = SEEK_SET; 3247191783Srmacklem fl->l_start = lop->nfslo_first; 3248191783Srmacklem if (lop->nfslo_end == NFS64BITSSET) 3249191783Srmacklem fl->l_len = 0; 3250191783Srmacklem else 3251191783Srmacklem fl->l_len = lop->nfslo_end - lop->nfslo_first; 3252191783Srmacklem fl->l_pid = (pid_t)0; 3253191783Srmacklem fl->l_type = lop->nfslo_type; 3254201439Srmacklem error = -1; /* no RPC required */ 3255191783Srmacklem } else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) || 3256191783Srmacklem fl->l_type == F_RDLCK)) { 3257191783Srmacklem /* 3258191783Srmacklem * The delegation ensures that there isn't a conflicting 3259191783Srmacklem * lock on the server, so return -1 to indicate an RPC 3260191783Srmacklem * isn't required. 3261191783Srmacklem */ 3262191783Srmacklem fl->l_type = F_UNLCK; 3263191783Srmacklem error = -1; 3264191783Srmacklem } 3265191783Srmacklem NFSUNLOCKCLSTATE(); 3266191783Srmacklem return (error); 3267191783Srmacklem} 3268191783Srmacklem 3269191783Srmacklem/* 3270191783Srmacklem * Handle Recall of a delegation. 3271191783Srmacklem * The clp must be exclusive locked when this is called. 3272191783Srmacklem */ 3273191783Srmacklemstatic int 3274191783Srmacklemnfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp, 3275191783Srmacklem struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p) 3276191783Srmacklem{ 3277191783Srmacklem struct nfsclowner *owp, *lowp, *nowp; 3278191783Srmacklem struct nfsclopen *op, *lop; 3279191783Srmacklem struct nfscllockowner *lp; 3280191783Srmacklem struct nfscllock *lckp; 3281191783Srmacklem struct nfsnode *np; 3282191783Srmacklem int error = 0, ret, gotvp = 0; 3283191783Srmacklem 3284191783Srmacklem if (vp == NULL) { 3285191783Srmacklem /* 3286191783Srmacklem * First, get a vnode for the file. This is needed to do RPCs. 3287191783Srmacklem */ 3288191783Srmacklem ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh, 3289191783Srmacklem dp->nfsdl_fhlen, p, &np); 3290191783Srmacklem if (ret) { 3291191783Srmacklem /* 3292191783Srmacklem * File isn't open, so nothing to move over to the 3293191783Srmacklem * server. 3294191783Srmacklem */ 3295191783Srmacklem return (0); 3296191783Srmacklem } 3297191783Srmacklem vp = NFSTOV(np); 3298191783Srmacklem gotvp = 1; 3299191783Srmacklem } else { 3300191783Srmacklem np = VTONFS(vp); 3301191783Srmacklem } 3302191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET; 3303191783Srmacklem NFSINVALATTRCACHE(np); 3304191783Srmacklem 3305191783Srmacklem /* 3306191783Srmacklem * Ok, if it's a write delegation, flush data to the server, so 3307191783Srmacklem * that close/open consistency is retained. 3308191783Srmacklem */ 3309191783Srmacklem NFSLOCKNODE(np); 3310191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) { 3311191783Srmacklem#ifdef APPLE 3312191783Srmacklem OSBitOrAtomic((u_int32_t)NDELEGRECALL, (UInt32 *)&np->n_flag); 3313191783Srmacklem#else 3314191783Srmacklem np->n_flag |= NDELEGRECALL; 3315191783Srmacklem#endif 3316191783Srmacklem NFSUNLOCKNODE(np); 3317191783Srmacklem (void) ncl_flush(vp, MNT_WAIT, cred, p, 1); 3318191783Srmacklem NFSLOCKNODE(np); 3319191783Srmacklem#ifdef APPLE 3320191783Srmacklem OSBitAndAtomic((int32_t)~(NMODIFIED | NDELEGRECALL), (UInt32 *)&np->n_flag); 3321191783Srmacklem#else 3322191783Srmacklem np->n_flag &= ~(NMODIFIED | NDELEGRECALL); 3323191783Srmacklem#endif 3324191783Srmacklem } 3325191783Srmacklem NFSUNLOCKNODE(np); 3326191783Srmacklem 3327191783Srmacklem /* 3328191783Srmacklem * Now, for each openowner with opens issued locally, move them 3329191783Srmacklem * over to state against the server. 3330191783Srmacklem */ 3331191783Srmacklem LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) { 3332191783Srmacklem lop = LIST_FIRST(&lowp->nfsow_open); 3333191783Srmacklem if (lop != NULL) { 3334191783Srmacklem if (LIST_NEXT(lop, nfso_list) != NULL) 3335191783Srmacklem panic("nfsdlg mult opens"); 3336191783Srmacklem /* 3337191783Srmacklem * Look for the same openowner against the server. 3338191783Srmacklem */ 3339191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 3340191783Srmacklem if (!NFSBCMP(lowp->nfsow_owner, 3341191783Srmacklem owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) { 3342191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3343191783Srmacklem ret = nfscl_moveopen(vp, clp, nmp, lop, 3344191783Srmacklem owp, dp, cred, p); 3345191783Srmacklem if (ret == NFSERR_STALECLIENTID || 3346191783Srmacklem ret == NFSERR_STALEDONTRECOVER) { 3347191783Srmacklem if (gotvp) 3348191783Srmacklem vrele(vp); 3349191783Srmacklem return (ret); 3350191783Srmacklem } 3351191783Srmacklem if (ret) { 3352191783Srmacklem nfscl_freeopen(lop, 1); 3353191783Srmacklem if (!error) 3354191783Srmacklem error = ret; 3355191783Srmacklem } 3356191783Srmacklem break; 3357191783Srmacklem } 3358191783Srmacklem } 3359191783Srmacklem 3360191783Srmacklem /* 3361191783Srmacklem * If no openowner found, create one and get an open 3362191783Srmacklem * for it. 3363191783Srmacklem */ 3364191783Srmacklem if (owp == NULL) { 3365191783Srmacklem MALLOC(nowp, struct nfsclowner *, 3366191783Srmacklem sizeof (struct nfsclowner), M_NFSCLOWNER, 3367191783Srmacklem M_WAITOK); 3368191783Srmacklem nfscl_newopen(clp, NULL, &owp, &nowp, &op, 3369191783Srmacklem NULL, lowp->nfsow_owner, dp->nfsdl_fh, 3370191783Srmacklem dp->nfsdl_fhlen, NULL); 3371191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3372191783Srmacklem ret = nfscl_moveopen(vp, clp, nmp, lop, 3373191783Srmacklem owp, dp, cred, p); 3374191783Srmacklem if (ret) { 3375191783Srmacklem nfscl_freeopenowner(owp, 0); 3376191783Srmacklem if (ret == NFSERR_STALECLIENTID || 3377191783Srmacklem ret == NFSERR_STALEDONTRECOVER) { 3378191783Srmacklem if (gotvp) 3379191783Srmacklem vrele(vp); 3380191783Srmacklem return (ret); 3381191783Srmacklem } 3382191783Srmacklem if (ret) { 3383191783Srmacklem nfscl_freeopen(lop, 1); 3384191783Srmacklem if (!error) 3385191783Srmacklem error = ret; 3386191783Srmacklem } 3387191783Srmacklem } 3388191783Srmacklem } 3389191783Srmacklem } 3390191783Srmacklem } 3391191783Srmacklem 3392191783Srmacklem /* 3393191783Srmacklem * Now, get byte range locks for any locks done locally. 3394191783Srmacklem */ 3395191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 3396191783Srmacklem LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) { 3397191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3398191783Srmacklem ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p); 3399191783Srmacklem if (ret == NFSERR_STALESTATEID || 3400191783Srmacklem ret == NFSERR_STALEDONTRECOVER || 3401191783Srmacklem ret == NFSERR_STALECLIENTID) { 3402191783Srmacklem if (gotvp) 3403191783Srmacklem vrele(vp); 3404191783Srmacklem return (ret); 3405191783Srmacklem } 3406191783Srmacklem if (ret && !error) 3407191783Srmacklem error = ret; 3408191783Srmacklem } 3409191783Srmacklem } 3410191783Srmacklem if (gotvp) 3411191783Srmacklem vrele(vp); 3412191783Srmacklem return (error); 3413191783Srmacklem} 3414191783Srmacklem 3415191783Srmacklem/* 3416191783Srmacklem * Move a locally issued open over to an owner on the state list. 3417191783Srmacklem * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and 3418191783Srmacklem * returns with it unlocked. 3419191783Srmacklem */ 3420191783Srmacklemstatic int 3421191783Srmacklemnfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp, 3422191783Srmacklem struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp, 3423191783Srmacklem struct ucred *cred, NFSPROC_T *p) 3424191783Srmacklem{ 3425191783Srmacklem struct nfsclopen *op, *nop; 3426191783Srmacklem struct nfscldeleg *ndp; 3427191783Srmacklem struct nfsnode *np; 3428191783Srmacklem int error = 0, newone; 3429191783Srmacklem 3430191783Srmacklem /* 3431191783Srmacklem * First, look for an appropriate open, If found, just increment the 3432191783Srmacklem * opencnt in it. 3433191783Srmacklem */ 3434191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 3435191783Srmacklem if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode && 3436191783Srmacklem op->nfso_fhlen == lop->nfso_fhlen && 3437191783Srmacklem !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) { 3438191783Srmacklem op->nfso_opencnt += lop->nfso_opencnt; 3439191783Srmacklem nfscl_freeopen(lop, 1); 3440191783Srmacklem return (0); 3441191783Srmacklem } 3442191783Srmacklem } 3443191783Srmacklem 3444191783Srmacklem /* No appropriate open, so we have to do one against the server. */ 3445191783Srmacklem np = VTONFS(vp); 3446191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 3447191783Srmacklem lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK); 3448191783Srmacklem newone = 0; 3449191783Srmacklem nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner, 3450191783Srmacklem lop->nfso_fh, lop->nfso_fhlen, &newone); 3451191783Srmacklem ndp = dp; 3452191783Srmacklem error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen, 3453191783Srmacklem lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op, 3454191783Srmacklem NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p); 3455191783Srmacklem if (error) { 3456191783Srmacklem if (newone) 3457191783Srmacklem nfscl_freeopen(op, 0); 3458191783Srmacklem } else { 3459191783Srmacklem if (newone) 3460191783Srmacklem newnfs_copyincred(cred, &op->nfso_cred); 3461191783Srmacklem op->nfso_mode |= lop->nfso_mode; 3462191783Srmacklem op->nfso_opencnt += lop->nfso_opencnt; 3463191783Srmacklem nfscl_freeopen(lop, 1); 3464191783Srmacklem } 3465191783Srmacklem if (nop != NULL) 3466191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 3467191783Srmacklem if (ndp != NULL) { 3468191783Srmacklem /* 3469191783Srmacklem * What should I do with the returned delegation, since the 3470191783Srmacklem * delegation is being recalled? For now, just printf and 3471191783Srmacklem * through it away. 3472191783Srmacklem */ 3473191783Srmacklem printf("Moveopen returned deleg\n"); 3474191783Srmacklem FREE((caddr_t)ndp, M_NFSCLDELEG); 3475191783Srmacklem } 3476191783Srmacklem return (error); 3477191783Srmacklem} 3478191783Srmacklem 3479191783Srmacklem/* 3480191783Srmacklem * Recall all delegations on this client. 3481191783Srmacklem */ 3482191783Srmacklemstatic void 3483191783Srmacklemnfscl_totalrecall(struct nfsclclient *clp) 3484191783Srmacklem{ 3485191783Srmacklem struct nfscldeleg *dp; 3486191783Srmacklem 3487191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) 3488191783Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 3489191783Srmacklem} 3490191783Srmacklem 3491191783Srmacklem/* 3492191783Srmacklem * Relock byte ranges. Called for delegation recall and state expiry. 3493191783Srmacklem */ 3494191783Srmacklemstatic int 3495191783Srmacklemnfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp, 3496191783Srmacklem struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred, 3497191783Srmacklem NFSPROC_T *p) 3498191783Srmacklem{ 3499191783Srmacklem struct nfscllockowner *nlp; 3500191783Srmacklem struct nfsfh *nfhp; 3501191783Srmacklem u_int64_t off, len; 3502191783Srmacklem u_int32_t clidrev = 0; 3503191783Srmacklem int error, newone, donelocally; 3504191783Srmacklem 3505191783Srmacklem off = lop->nfslo_first; 3506191783Srmacklem len = lop->nfslo_end - lop->nfslo_first; 3507191783Srmacklem error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p, 3508191783Srmacklem clp, 1, lp->nfsl_owner, lp->nfsl_openowner, &nlp, &newone, 3509191783Srmacklem &donelocally); 3510191783Srmacklem if (error || donelocally) 3511191783Srmacklem return (error); 3512191783Srmacklem if (nmp->nm_clp != NULL) 3513191783Srmacklem clidrev = nmp->nm_clp->nfsc_clientidrev; 3514191783Srmacklem else 3515191783Srmacklem clidrev = 0; 3516191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 3517191783Srmacklem error = nfscl_trylock(nmp, vp, nfhp->nfh_fh, 3518191783Srmacklem nfhp->nfh_len, nlp, newone, 0, off, 3519191783Srmacklem len, lop->nfslo_type, cred, p); 3520191783Srmacklem if (error) 3521191783Srmacklem nfscl_freelockowner(nlp, 0); 3522191783Srmacklem return (error); 3523191783Srmacklem} 3524191783Srmacklem 3525191783Srmacklem/* 3526191783Srmacklem * Called to re-open a file. Basically get a vnode for the file handle 3527191783Srmacklem * and then call nfsrpc_openrpc() to do the rest. 3528191783Srmacklem */ 3529191783Srmacklemstatic int 3530191783Srmacklemnfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, 3531191783Srmacklem u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp, 3532191783Srmacklem struct ucred *cred, NFSPROC_T *p) 3533191783Srmacklem{ 3534191783Srmacklem struct nfsnode *np; 3535191783Srmacklem vnode_t vp; 3536191783Srmacklem int error; 3537191783Srmacklem 3538191783Srmacklem error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np); 3539191783Srmacklem if (error) 3540191783Srmacklem return (error); 3541191783Srmacklem vp = NFSTOV(np); 3542191783Srmacklem if (np->n_v4 != NULL) { 3543191783Srmacklem error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, 3544191783Srmacklem np->n_v4->n4_fhlen, fhp, fhlen, mode, op, 3545191783Srmacklem NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0, 3546191783Srmacklem cred, p); 3547191783Srmacklem } else { 3548191783Srmacklem error = EINVAL; 3549191783Srmacklem } 3550191783Srmacklem vrele(vp); 3551191783Srmacklem return (error); 3552191783Srmacklem} 3553191783Srmacklem 3554191783Srmacklem/* 3555191783Srmacklem * Try an open against the server. Just call nfsrpc_openrpc(), retrying while 3556191783Srmacklem * NFSERR_DELAY. Also, try system credentials, if the passed in credentials 3557191783Srmacklem * fail. 3558191783Srmacklem */ 3559191783Srmacklemstatic int 3560191783Srmacklemnfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen, 3561191783Srmacklem u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op, 3562191783Srmacklem u_int8_t *name, int namelen, struct nfscldeleg **ndpp, 3563191783Srmacklem int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p) 3564191783Srmacklem{ 3565191783Srmacklem int error; 3566191783Srmacklem 3567191783Srmacklem do { 3568191783Srmacklem error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen, 3569191783Srmacklem mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p, 3570191783Srmacklem 0, 0); 3571191783Srmacklem if (error == NFSERR_DELAY) 3572191783Srmacklem (void) nfs_catnap(PZERO, "nfstryop"); 3573191783Srmacklem } while (error == NFSERR_DELAY); 3574191783Srmacklem if (error == EAUTH || error == EACCES) { 3575191783Srmacklem /* Try again using system credentials */ 3576191783Srmacklem newnfs_setroot(cred); 3577191783Srmacklem do { 3578191783Srmacklem error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, 3579191783Srmacklem newfhlen, mode, op, name, namelen, ndpp, reclaim, 3580191783Srmacklem delegtype, cred, p, 1, 0); 3581191783Srmacklem if (error == NFSERR_DELAY) 3582191783Srmacklem (void) nfs_catnap(PZERO, "nfstryop"); 3583191783Srmacklem } while (error == NFSERR_DELAY); 3584191783Srmacklem } 3585191783Srmacklem return (error); 3586191783Srmacklem} 3587191783Srmacklem 3588191783Srmacklem/* 3589191783Srmacklem * Try a byte range lock. Just loop on nfsrpc_lock() while it returns 3590191783Srmacklem * NFSERR_DELAY. Also, retry with system credentials, if the provided 3591191783Srmacklem * cred don't work. 3592191783Srmacklem */ 3593191783Srmacklemstatic int 3594191783Srmacklemnfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, 3595191783Srmacklem int fhlen, struct nfscllockowner *nlp, int newone, int reclaim, 3596191783Srmacklem u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p) 3597191783Srmacklem{ 3598191783Srmacklem struct nfsrv_descript nfsd, *nd = &nfsd; 3599191783Srmacklem int error; 3600191783Srmacklem 3601191783Srmacklem do { 3602191783Srmacklem error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone, 3603191783Srmacklem reclaim, off, len, type, cred, p, 0); 3604191783Srmacklem if (!error && nd->nd_repstat == NFSERR_DELAY) 3605191783Srmacklem (void) nfs_catnap(PZERO, "nfstrylck"); 3606191783Srmacklem } while (!error && nd->nd_repstat == NFSERR_DELAY); 3607191783Srmacklem if (!error) 3608191783Srmacklem error = nd->nd_repstat; 3609191783Srmacklem if (error == EAUTH || error == EACCES) { 3610191783Srmacklem /* Try again using root credentials */ 3611191783Srmacklem newnfs_setroot(cred); 3612191783Srmacklem do { 3613191783Srmacklem error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, 3614191783Srmacklem newone, reclaim, off, len, type, cred, p, 1); 3615191783Srmacklem if (!error && nd->nd_repstat == NFSERR_DELAY) 3616191783Srmacklem (void) nfs_catnap(PZERO, "nfstrylck"); 3617191783Srmacklem } while (!error && nd->nd_repstat == NFSERR_DELAY); 3618191783Srmacklem if (!error) 3619191783Srmacklem error = nd->nd_repstat; 3620191783Srmacklem } 3621191783Srmacklem return (error); 3622191783Srmacklem} 3623191783Srmacklem 3624191783Srmacklem/* 3625191783Srmacklem * Try a delegreturn against the server. Just call nfsrpc_delegreturn(), 3626191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in 3627191783Srmacklem * credentials fail. 3628191783Srmacklem */ 3629191783Srmacklemstatic int 3630191783Srmacklemnfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred, 3631191783Srmacklem struct nfsmount *nmp, NFSPROC_T *p) 3632191783Srmacklem{ 3633191783Srmacklem int error; 3634191783Srmacklem 3635191783Srmacklem do { 3636191783Srmacklem error = nfsrpc_delegreturn(dp, cred, nmp, p, 0); 3637191783Srmacklem if (error == NFSERR_DELAY) 3638191783Srmacklem (void) nfs_catnap(PZERO, "nfstrydp"); 3639191783Srmacklem } while (error == NFSERR_DELAY); 3640191783Srmacklem if (error == EAUTH || error == EACCES) { 3641191783Srmacklem /* Try again using system credentials */ 3642191783Srmacklem newnfs_setroot(cred); 3643191783Srmacklem do { 3644191783Srmacklem error = nfsrpc_delegreturn(dp, cred, nmp, p, 1); 3645191783Srmacklem if (error == NFSERR_DELAY) 3646191783Srmacklem (void) nfs_catnap(PZERO, "nfstrydp"); 3647191783Srmacklem } while (error == NFSERR_DELAY); 3648191783Srmacklem } 3649191783Srmacklem return (error); 3650191783Srmacklem} 3651191783Srmacklem 3652191783Srmacklem/* 3653191783Srmacklem * Try a close against the server. Just call nfsrpc_closerpc(), 3654191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in 3655191783Srmacklem * credentials fail. 3656191783Srmacklem */ 3657191783SrmacklemAPPLESTATIC int 3658191783Srmacklemnfscl_tryclose(struct nfsclopen *op, struct ucred *cred, 3659191783Srmacklem struct nfsmount *nmp, NFSPROC_T *p) 3660191783Srmacklem{ 3661191783Srmacklem struct nfsrv_descript nfsd, *nd = &nfsd; 3662191783Srmacklem int error; 3663191783Srmacklem 3664191783Srmacklem do { 3665191783Srmacklem error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0); 3666191783Srmacklem if (error == NFSERR_DELAY) 3667191783Srmacklem (void) nfs_catnap(PZERO, "nfstrycl"); 3668191783Srmacklem } while (error == NFSERR_DELAY); 3669191783Srmacklem if (error == EAUTH || error == EACCES) { 3670191783Srmacklem /* Try again using system credentials */ 3671191783Srmacklem newnfs_setroot(cred); 3672191783Srmacklem do { 3673191783Srmacklem error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1); 3674191783Srmacklem if (error == NFSERR_DELAY) 3675191783Srmacklem (void) nfs_catnap(PZERO, "nfstrycl"); 3676191783Srmacklem } while (error == NFSERR_DELAY); 3677191783Srmacklem } 3678191783Srmacklem return (error); 3679191783Srmacklem} 3680191783Srmacklem 3681191783Srmacklem/* 3682191783Srmacklem * Decide if a delegation on a file permits close without flushing writes 3683191783Srmacklem * to the server. This might be a big performance win in some environments. 3684191783Srmacklem * (Not useful until the client does caching on local stable storage.) 3685191783Srmacklem */ 3686191783SrmacklemAPPLESTATIC int 3687191783Srmacklemnfscl_mustflush(vnode_t vp) 3688191783Srmacklem{ 3689191783Srmacklem struct nfsclclient *clp; 3690191783Srmacklem struct nfscldeleg *dp; 3691191783Srmacklem struct nfsnode *np; 3692191783Srmacklem struct nfsmount *nmp; 3693191783Srmacklem 3694191783Srmacklem np = VTONFS(vp); 3695191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 3696191783Srmacklem if (!NFSHASNFSV4(nmp)) 3697191783Srmacklem return (1); 3698191783Srmacklem NFSLOCKCLSTATE(); 3699191783Srmacklem clp = nfscl_findcl(nmp); 3700191783Srmacklem if (clp == NULL) { 3701191783Srmacklem NFSUNLOCKCLSTATE(); 3702191783Srmacklem return (1); 3703191783Srmacklem } 3704191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 3705191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_RECALL)) 3706191783Srmacklem == NFSCLDL_WRITE && 3707191783Srmacklem (dp->nfsdl_sizelimit >= np->n_size || 3708191783Srmacklem !NFSHASSTRICT3530(nmp))) { 3709191783Srmacklem NFSUNLOCKCLSTATE(); 3710191783Srmacklem return (0); 3711191783Srmacklem } 3712191783Srmacklem NFSUNLOCKCLSTATE(); 3713191783Srmacklem return (1); 3714191783Srmacklem} 3715191783Srmacklem 3716191783Srmacklem/* 3717191783Srmacklem * See if a (write) delegation exists for this file. 3718191783Srmacklem */ 3719191783SrmacklemAPPLESTATIC int 3720191783Srmacklemnfscl_nodeleg(vnode_t vp, int writedeleg) 3721191783Srmacklem{ 3722191783Srmacklem struct nfsclclient *clp; 3723191783Srmacklem struct nfscldeleg *dp; 3724191783Srmacklem struct nfsnode *np; 3725191783Srmacklem struct nfsmount *nmp; 3726191783Srmacklem 3727191783Srmacklem np = VTONFS(vp); 3728191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 3729191783Srmacklem if (!NFSHASNFSV4(nmp)) 3730191783Srmacklem return (1); 3731191783Srmacklem NFSLOCKCLSTATE(); 3732191783Srmacklem clp = nfscl_findcl(nmp); 3733191783Srmacklem if (clp == NULL) { 3734191783Srmacklem NFSUNLOCKCLSTATE(); 3735191783Srmacklem return (1); 3736191783Srmacklem } 3737191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 3738191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_RECALL) == 0 && 3739191783Srmacklem (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) 3740191783Srmacklem == NFSCLDL_WRITE)) { 3741191783Srmacklem NFSUNLOCKCLSTATE(); 3742191783Srmacklem return (0); 3743191783Srmacklem } 3744191783Srmacklem NFSUNLOCKCLSTATE(); 3745191783Srmacklem return (1); 3746191783Srmacklem} 3747191783Srmacklem 3748191783Srmacklem/* 3749191783Srmacklem * Look for an associated delegation that should be DelegReturned. 3750191783Srmacklem */ 3751191783SrmacklemAPPLESTATIC int 3752191783Srmacklemnfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp) 3753191783Srmacklem{ 3754191783Srmacklem struct nfsclclient *clp; 3755191783Srmacklem struct nfscldeleg *dp; 3756191783Srmacklem struct nfsclowner *owp; 3757191783Srmacklem struct nfscllockowner *lp; 3758191783Srmacklem struct nfsmount *nmp; 3759191783Srmacklem struct ucred *cred; 3760191783Srmacklem struct nfsnode *np; 3761191783Srmacklem int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept; 3762191783Srmacklem 3763191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 3764191783Srmacklem np = VTONFS(vp); 3765191783Srmacklem NFSLOCKCLSTATE(); 3766191783Srmacklem /* 3767191783Srmacklem * Loop around waiting for: 3768191783Srmacklem * - outstanding I/O operations on delegations to complete 3769191783Srmacklem * - for a delegation on vp that has state, lock the client and 3770191783Srmacklem * do a recall 3771191783Srmacklem * - return delegation with no state 3772191783Srmacklem */ 3773191783Srmacklem while (1) { 3774191783Srmacklem clp = nfscl_findcl(nmp); 3775191783Srmacklem if (clp == NULL) { 3776191783Srmacklem NFSUNLOCKCLSTATE(); 3777191783Srmacklem return (retcnt); 3778191783Srmacklem } 3779191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 3780191783Srmacklem np->n_fhp->nfh_len); 3781191783Srmacklem if (dp != NULL) { 3782191783Srmacklem /* 3783191783Srmacklem * Wait for outstanding I/O ops to be done. 3784191783Srmacklem */ 3785191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 3786191783Srmacklem if (igotlock) { 3787191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3788191783Srmacklem igotlock = 0; 3789191783Srmacklem } 3790191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 3791191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 3792191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 3793191783Srmacklem continue; 3794191783Srmacklem } 3795191783Srmacklem needsrecall = 0; 3796191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 3797191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 3798191783Srmacklem needsrecall = 1; 3799191783Srmacklem break; 3800191783Srmacklem } 3801191783Srmacklem } 3802191783Srmacklem if (!needsrecall) { 3803191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 3804191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 3805191783Srmacklem needsrecall = 1; 3806191783Srmacklem break; 3807191783Srmacklem } 3808191783Srmacklem } 3809191783Srmacklem } 3810191783Srmacklem if (needsrecall && !triedrecall) { 3811191783Srmacklem islept = 0; 3812191783Srmacklem while (!igotlock) { 3813191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 3814191783Srmacklem &islept, NFSCLSTATEMUTEXPTR); 3815191783Srmacklem if (islept) 3816191783Srmacklem break; 3817191783Srmacklem } 3818191783Srmacklem if (islept) 3819191783Srmacklem continue; 3820191783Srmacklem NFSUNLOCKCLSTATE(); 3821191783Srmacklem cred = newnfs_getcred(); 3822191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3823191783Srmacklem (void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p); 3824191783Srmacklem NFSFREECRED(cred); 3825191783Srmacklem triedrecall = 1; 3826191783Srmacklem NFSLOCKCLSTATE(); 3827191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3828191783Srmacklem igotlock = 0; 3829191783Srmacklem continue; 3830191783Srmacklem } 3831191783Srmacklem *stp = dp->nfsdl_stateid; 3832191783Srmacklem retcnt = 1; 3833191783Srmacklem nfscl_cleandeleg(dp); 3834191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 3835191783Srmacklem } 3836191783Srmacklem if (igotlock) 3837191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3838191783Srmacklem NFSUNLOCKCLSTATE(); 3839191783Srmacklem return (retcnt); 3840191783Srmacklem } 3841191783Srmacklem} 3842191783Srmacklem 3843191783Srmacklem/* 3844191783Srmacklem * Look for associated delegation(s) that should be DelegReturned. 3845191783Srmacklem */ 3846191783SrmacklemAPPLESTATIC int 3847191783Srmacklemnfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp, 3848191783Srmacklem nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p) 3849191783Srmacklem{ 3850191783Srmacklem struct nfsclclient *clp; 3851191783Srmacklem struct nfscldeleg *dp; 3852191783Srmacklem struct nfsclowner *owp; 3853191783Srmacklem struct nfscllockowner *lp; 3854191783Srmacklem struct nfsmount *nmp; 3855191783Srmacklem struct ucred *cred; 3856191783Srmacklem struct nfsnode *np; 3857191783Srmacklem int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept; 3858191783Srmacklem 3859191783Srmacklem nmp = VFSTONFS(vnode_mount(fvp)); 3860191783Srmacklem *gotfdp = 0; 3861191783Srmacklem *gottdp = 0; 3862191783Srmacklem NFSLOCKCLSTATE(); 3863191783Srmacklem /* 3864191783Srmacklem * Loop around waiting for: 3865191783Srmacklem * - outstanding I/O operations on delegations to complete 3866191783Srmacklem * - for a delegation on fvp that has state, lock the client and 3867191783Srmacklem * do a recall 3868191783Srmacklem * - return delegation(s) with no state. 3869191783Srmacklem */ 3870191783Srmacklem while (1) { 3871191783Srmacklem clp = nfscl_findcl(nmp); 3872191783Srmacklem if (clp == NULL) { 3873191783Srmacklem NFSUNLOCKCLSTATE(); 3874191783Srmacklem return (retcnt); 3875191783Srmacklem } 3876191783Srmacklem np = VTONFS(fvp); 3877191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 3878191783Srmacklem np->n_fhp->nfh_len); 3879191783Srmacklem if (dp != NULL && *gotfdp == 0) { 3880191783Srmacklem /* 3881191783Srmacklem * Wait for outstanding I/O ops to be done. 3882191783Srmacklem */ 3883191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 3884191783Srmacklem if (igotlock) { 3885191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3886191783Srmacklem igotlock = 0; 3887191783Srmacklem } 3888191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 3889191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 3890191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 3891191783Srmacklem continue; 3892191783Srmacklem } 3893191783Srmacklem needsrecall = 0; 3894191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 3895191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 3896191783Srmacklem needsrecall = 1; 3897191783Srmacklem break; 3898191783Srmacklem } 3899191783Srmacklem } 3900191783Srmacklem if (!needsrecall) { 3901191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 3902191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 3903191783Srmacklem needsrecall = 1; 3904191783Srmacklem break; 3905191783Srmacklem } 3906191783Srmacklem } 3907191783Srmacklem } 3908191783Srmacklem if (needsrecall && !triedrecall) { 3909191783Srmacklem islept = 0; 3910191783Srmacklem while (!igotlock) { 3911191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 3912191783Srmacklem &islept, NFSCLSTATEMUTEXPTR); 3913191783Srmacklem if (islept) 3914191783Srmacklem break; 3915191783Srmacklem } 3916191783Srmacklem if (islept) 3917191783Srmacklem continue; 3918191783Srmacklem NFSUNLOCKCLSTATE(); 3919191783Srmacklem cred = newnfs_getcred(); 3920191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3921191783Srmacklem (void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p); 3922191783Srmacklem NFSFREECRED(cred); 3923191783Srmacklem triedrecall = 1; 3924191783Srmacklem NFSLOCKCLSTATE(); 3925191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3926191783Srmacklem igotlock = 0; 3927191783Srmacklem continue; 3928191783Srmacklem } 3929191783Srmacklem *fstp = dp->nfsdl_stateid; 3930191783Srmacklem retcnt++; 3931191783Srmacklem *gotfdp = 1; 3932191783Srmacklem nfscl_cleandeleg(dp); 3933191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 3934191783Srmacklem } 3935191783Srmacklem if (igotlock) { 3936191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3937191783Srmacklem igotlock = 0; 3938191783Srmacklem } 3939191783Srmacklem if (tvp != NULL) { 3940191783Srmacklem np = VTONFS(tvp); 3941191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 3942191783Srmacklem np->n_fhp->nfh_len); 3943191783Srmacklem if (dp != NULL && *gottdp == 0) { 3944191783Srmacklem /* 3945191783Srmacklem * Wait for outstanding I/O ops to be done. 3946191783Srmacklem */ 3947191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 3948191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 3949191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 3950191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 3951191783Srmacklem continue; 3952191783Srmacklem } 3953191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 3954191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 3955191783Srmacklem NFSUNLOCKCLSTATE(); 3956191783Srmacklem return (retcnt); 3957191783Srmacklem } 3958191783Srmacklem } 3959191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 3960191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 3961191783Srmacklem NFSUNLOCKCLSTATE(); 3962191783Srmacklem return (retcnt); 3963191783Srmacklem } 3964191783Srmacklem } 3965191783Srmacklem *tstp = dp->nfsdl_stateid; 3966191783Srmacklem retcnt++; 3967191783Srmacklem *gottdp = 1; 3968191783Srmacklem nfscl_cleandeleg(dp); 3969191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 3970191783Srmacklem } 3971191783Srmacklem } 3972191783Srmacklem NFSUNLOCKCLSTATE(); 3973191783Srmacklem return (retcnt); 3974191783Srmacklem } 3975191783Srmacklem} 3976191783Srmacklem 3977191783Srmacklem/* 3978191783Srmacklem * Get a reference on the clientid associated with the mount point. 3979191783Srmacklem * Return 1 if success, 0 otherwise. 3980191783Srmacklem */ 3981191783SrmacklemAPPLESTATIC int 3982191783Srmacklemnfscl_getref(struct nfsmount *nmp) 3983191783Srmacklem{ 3984191783Srmacklem struct nfsclclient *clp; 3985191783Srmacklem 3986191783Srmacklem NFSLOCKCLSTATE(); 3987191783Srmacklem clp = nfscl_findcl(nmp); 3988191783Srmacklem if (clp == NULL) { 3989191783Srmacklem NFSUNLOCKCLSTATE(); 3990191783Srmacklem return (0); 3991191783Srmacklem } 3992191783Srmacklem nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR); 3993191783Srmacklem NFSUNLOCKCLSTATE(); 3994191783Srmacklem return (1); 3995191783Srmacklem} 3996191783Srmacklem 3997191783Srmacklem/* 3998191783Srmacklem * Release a reference on a clientid acquired with the above call. 3999191783Srmacklem */ 4000191783SrmacklemAPPLESTATIC void 4001191783Srmacklemnfscl_relref(struct nfsmount *nmp) 4002191783Srmacklem{ 4003191783Srmacklem struct nfsclclient *clp; 4004191783Srmacklem 4005191783Srmacklem NFSLOCKCLSTATE(); 4006191783Srmacklem clp = nfscl_findcl(nmp); 4007191783Srmacklem if (clp == NULL) { 4008191783Srmacklem NFSUNLOCKCLSTATE(); 4009191783Srmacklem return; 4010191783Srmacklem } 4011191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 4012191783Srmacklem NFSUNLOCKCLSTATE(); 4013191783Srmacklem} 4014191783Srmacklem 4015191783Srmacklem/* 4016191783Srmacklem * Save the size attribute in the delegation, since the nfsnode 4017191783Srmacklem * is going away. 4018191783Srmacklem */ 4019191783SrmacklemAPPLESTATIC void 4020191783Srmacklemnfscl_reclaimnode(vnode_t vp) 4021191783Srmacklem{ 4022191783Srmacklem struct nfsclclient *clp; 4023191783Srmacklem struct nfscldeleg *dp; 4024191783Srmacklem struct nfsnode *np = VTONFS(vp); 4025191783Srmacklem struct nfsmount *nmp; 4026191783Srmacklem 4027191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4028191783Srmacklem if (!NFSHASNFSV4(nmp)) 4029191783Srmacklem return; 4030191783Srmacklem NFSLOCKCLSTATE(); 4031191783Srmacklem clp = nfscl_findcl(nmp); 4032191783Srmacklem if (clp == NULL) { 4033191783Srmacklem NFSUNLOCKCLSTATE(); 4034191783Srmacklem return; 4035191783Srmacklem } 4036191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4037191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) 4038191783Srmacklem dp->nfsdl_size = np->n_size; 4039191783Srmacklem NFSUNLOCKCLSTATE(); 4040191783Srmacklem} 4041191783Srmacklem 4042191783Srmacklem/* 4043191783Srmacklem * Get the saved size attribute in the delegation, since it is a 4044191783Srmacklem * newly allocated nfsnode. 4045191783Srmacklem */ 4046191783SrmacklemAPPLESTATIC void 4047191783Srmacklemnfscl_newnode(vnode_t vp) 4048191783Srmacklem{ 4049191783Srmacklem struct nfsclclient *clp; 4050191783Srmacklem struct nfscldeleg *dp; 4051191783Srmacklem struct nfsnode *np = VTONFS(vp); 4052191783Srmacklem struct nfsmount *nmp; 4053191783Srmacklem 4054191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4055191783Srmacklem if (!NFSHASNFSV4(nmp)) 4056191783Srmacklem return; 4057191783Srmacklem NFSLOCKCLSTATE(); 4058191783Srmacklem clp = nfscl_findcl(nmp); 4059191783Srmacklem if (clp == NULL) { 4060191783Srmacklem NFSUNLOCKCLSTATE(); 4061191783Srmacklem return; 4062191783Srmacklem } 4063191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4064191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) 4065191783Srmacklem np->n_size = dp->nfsdl_size; 4066191783Srmacklem NFSUNLOCKCLSTATE(); 4067191783Srmacklem} 4068191783Srmacklem 4069191783Srmacklem/* 4070191783Srmacklem * If there is a valid write delegation for this file, set the modtime 4071191783Srmacklem * to the local clock time. 4072191783Srmacklem */ 4073191783SrmacklemAPPLESTATIC void 4074191783Srmacklemnfscl_delegmodtime(vnode_t vp) 4075191783Srmacklem{ 4076191783Srmacklem struct nfsclclient *clp; 4077191783Srmacklem struct nfscldeleg *dp; 4078191783Srmacklem struct nfsnode *np = VTONFS(vp); 4079191783Srmacklem struct nfsmount *nmp; 4080191783Srmacklem 4081191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4082191783Srmacklem if (!NFSHASNFSV4(nmp)) 4083191783Srmacklem return; 4084191783Srmacklem NFSLOCKCLSTATE(); 4085191783Srmacklem clp = nfscl_findcl(nmp); 4086191783Srmacklem if (clp == NULL) { 4087191783Srmacklem NFSUNLOCKCLSTATE(); 4088191783Srmacklem return; 4089191783Srmacklem } 4090191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4091191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) { 4092191783Srmacklem NFSGETNANOTIME(&dp->nfsdl_modtime); 4093191783Srmacklem dp->nfsdl_flags |= NFSCLDL_MODTIMESET; 4094191783Srmacklem } 4095191783Srmacklem NFSUNLOCKCLSTATE(); 4096191783Srmacklem} 4097191783Srmacklem 4098191783Srmacklem/* 4099191783Srmacklem * If there is a valid write delegation for this file with a modtime set, 4100191783Srmacklem * put that modtime in mtime. 4101191783Srmacklem */ 4102191783SrmacklemAPPLESTATIC void 4103191783Srmacklemnfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime) 4104191783Srmacklem{ 4105191783Srmacklem struct nfsclclient *clp; 4106191783Srmacklem struct nfscldeleg *dp; 4107191783Srmacklem struct nfsnode *np = VTONFS(vp); 4108191783Srmacklem struct nfsmount *nmp; 4109191783Srmacklem 4110191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4111191783Srmacklem if (!NFSHASNFSV4(nmp)) 4112191783Srmacklem return; 4113191783Srmacklem NFSLOCKCLSTATE(); 4114191783Srmacklem clp = nfscl_findcl(nmp); 4115191783Srmacklem if (clp == NULL) { 4116191783Srmacklem NFSUNLOCKCLSTATE(); 4117191783Srmacklem return; 4118191783Srmacklem } 4119191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4120191783Srmacklem if (dp != NULL && 4121191783Srmacklem (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) == 4122191783Srmacklem (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) 4123191783Srmacklem *mtime = dp->nfsdl_modtime; 4124191783Srmacklem NFSUNLOCKCLSTATE(); 4125191783Srmacklem} 4126191783Srmacklem 4127191783Srmacklemstatic int 4128191783Srmacklemnfscl_errmap(struct nfsrv_descript *nd) 4129191783Srmacklem{ 4130191783Srmacklem short *defaulterrp, *errp; 4131191783Srmacklem 4132191783Srmacklem if (!nd->nd_repstat) 4133191783Srmacklem return (0); 4134191783Srmacklem if (nd->nd_procnum == NFSPROC_NOOP) 4135191783Srmacklem return (txdr_unsigned(nd->nd_repstat & 0xffff)); 4136191783Srmacklem if (nd->nd_repstat == EBADRPC) 4137191783Srmacklem return (txdr_unsigned(NFSERR_BADXDR)); 4138191783Srmacklem if (nd->nd_repstat == NFSERR_MINORVERMISMATCH || 4139191783Srmacklem nd->nd_repstat == NFSERR_OPILLEGAL) 4140191783Srmacklem return (txdr_unsigned(nd->nd_repstat)); 4141191783Srmacklem errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum]; 4142191783Srmacklem while (*++errp) 4143191783Srmacklem if (*errp == (short)nd->nd_repstat) 4144191783Srmacklem return (txdr_unsigned(nd->nd_repstat)); 4145191783Srmacklem return (txdr_unsigned(*defaulterrp)); 4146191783Srmacklem} 4147191783Srmacklem 4148