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$"); 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. 67229599Srmacklem * - The function nfscl_cleanup_common() is executed after a process exits. 68229599Srmacklem * It goes 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 *, 98223774Srmacklem u_int8_t *, u_int32_t, struct nfscllockowner **, 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_freealllocks(struct nfscllockownerhead *, int); 118201439Srmacklemstatic int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int, 119201439Srmacklem struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **); 120191783Srmacklemstatic void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *, 121191783Srmacklem struct nfsclowner **, struct nfsclowner **, struct nfsclopen **, 122191783Srmacklem struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *); 123191783Srmacklemstatic int nfscl_moveopen(vnode_t , struct nfsclclient *, 124191783Srmacklem struct nfsmount *, struct nfsclopen *, struct nfsclowner *, 125191783Srmacklem struct nfscldeleg *, struct ucred *, NFSPROC_T *); 126191783Srmacklemstatic void nfscl_totalrecall(struct nfsclclient *); 127191783Srmacklemstatic int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *, 128191783Srmacklem struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *); 129191783Srmacklemstatic int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int, 130191783Srmacklem u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int, 131191783Srmacklem struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *); 132191783Srmacklemstatic int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *, 133191783Srmacklem int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short, 134191783Srmacklem struct ucred *, NFSPROC_T *); 135191783Srmacklemstatic int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t, 136191783Srmacklem struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *); 137191783Srmacklemstatic void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *); 138191783Srmacklemstatic int nfscl_errmap(struct nfsrv_descript *); 139191783Srmacklemstatic void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *); 140191783Srmacklemstatic int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *, 141207082Srmacklem struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int); 142191783Srmacklemstatic void nfscl_freeopenowner(struct nfsclowner *, int); 143191783Srmacklemstatic void nfscl_cleandeleg(struct nfscldeleg *); 144191783Srmacklemstatic int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *, 145191783Srmacklem struct nfsmount *, NFSPROC_T *); 146229752Srmacklemstatic void nfscl_emptylockowner(struct nfscllockowner *, 147229752Srmacklem struct nfscllockownerfhhead *); 148191783Srmacklem 149191783Srmacklemstatic short nfscberr_null[] = { 150191783Srmacklem 0, 151191783Srmacklem 0, 152191783Srmacklem}; 153191783Srmacklem 154191783Srmacklemstatic short nfscberr_getattr[] = { 155191783Srmacklem NFSERR_RESOURCE, 156191783Srmacklem NFSERR_BADHANDLE, 157191783Srmacklem NFSERR_BADXDR, 158191783Srmacklem NFSERR_RESOURCE, 159191783Srmacklem NFSERR_SERVERFAULT, 160191783Srmacklem 0, 161191783Srmacklem}; 162191783Srmacklem 163191783Srmacklemstatic short nfscberr_recall[] = { 164191783Srmacklem NFSERR_RESOURCE, 165191783Srmacklem NFSERR_BADHANDLE, 166191783Srmacklem NFSERR_BADSTATEID, 167191783Srmacklem NFSERR_BADXDR, 168191783Srmacklem NFSERR_RESOURCE, 169191783Srmacklem NFSERR_SERVERFAULT, 170191783Srmacklem 0, 171191783Srmacklem}; 172191783Srmacklem 173191783Srmacklemstatic short *nfscl_cberrmap[] = { 174191783Srmacklem nfscberr_null, 175191783Srmacklem nfscberr_null, 176191783Srmacklem nfscberr_null, 177191783Srmacklem nfscberr_getattr, 178191783Srmacklem nfscberr_recall 179191783Srmacklem}; 180191783Srmacklem 181191783Srmacklem#define NETFAMILY(clp) \ 182191783Srmacklem (((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET) 183191783Srmacklem 184191783Srmacklem/* 185191783Srmacklem * Called for an open operation. 186191783Srmacklem * If the nfhp argument is NULL, just get an openowner. 187191783Srmacklem */ 188191783SrmacklemAPPLESTATIC int 189191783Srmacklemnfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg, 190191783Srmacklem struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp, 191191783Srmacklem struct nfsclopen **opp, int *newonep, int *retp, int lockit) 192191783Srmacklem{ 193191783Srmacklem struct nfsclclient *clp; 194191783Srmacklem struct nfsclowner *owp, *nowp; 195191783Srmacklem struct nfsclopen *op = NULL, *nop = NULL; 196191783Srmacklem struct nfscldeleg *dp; 197191783Srmacklem struct nfsclownerhead *ohp; 198191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 199191783Srmacklem int ret; 200191783Srmacklem 201191783Srmacklem if (newonep != NULL) 202191783Srmacklem *newonep = 0; 203191783Srmacklem if (opp != NULL) 204191783Srmacklem *opp = NULL; 205191783Srmacklem if (owpp != NULL) 206191783Srmacklem *owpp = NULL; 207191783Srmacklem 208191783Srmacklem /* 209191783Srmacklem * Might need one or both of these, so MALLOC them now, to 210191783Srmacklem * avoid a tsleep() in MALLOC later. 211191783Srmacklem */ 212191783Srmacklem MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner), 213191783Srmacklem M_NFSCLOWNER, M_WAITOK); 214191783Srmacklem if (nfhp != NULL) 215191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 216191783Srmacklem fhlen - 1, M_NFSCLOPEN, M_WAITOK); 217191783Srmacklem ret = nfscl_getcl(vp, cred, p, &clp); 218191783Srmacklem if (ret != 0) { 219191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 220191783Srmacklem if (nop != NULL) 221191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 222191783Srmacklem return (ret); 223191783Srmacklem } 224191783Srmacklem 225191783Srmacklem /* 226191783Srmacklem * Get the Open iff it already exists. 227191783Srmacklem * If none found, add the new one or return error, depending upon 228191783Srmacklem * "create". 229191783Srmacklem */ 230222719Srmacklem nfscl_filllockowner(p->td_proc, own, F_POSIX); 231191783Srmacklem NFSLOCKCLSTATE(); 232191783Srmacklem dp = NULL; 233191783Srmacklem /* First check the delegation list */ 234191783Srmacklem if (nfhp != NULL && usedeleg) { 235191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) { 236191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 237191783Srmacklem !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) { 238191783Srmacklem if (!(amode & NFSV4OPEN_ACCESSWRITE) || 239191783Srmacklem (dp->nfsdl_flags & NFSCLDL_WRITE)) 240191783Srmacklem break; 241191783Srmacklem dp = NULL; 242191783Srmacklem break; 243191783Srmacklem } 244191783Srmacklem } 245191783Srmacklem } 246191783Srmacklem 247191783Srmacklem if (dp != NULL) 248191783Srmacklem ohp = &dp->nfsdl_owner; 249191783Srmacklem else 250191783Srmacklem ohp = &clp->nfsc_owner; 251191783Srmacklem /* Now, search for an openowner */ 252191783Srmacklem LIST_FOREACH(owp, ohp, nfsow_list) { 253191783Srmacklem if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN)) 254191783Srmacklem break; 255191783Srmacklem } 256191783Srmacklem 257191783Srmacklem /* 258191783Srmacklem * Create a new open, as required. 259191783Srmacklem */ 260191783Srmacklem nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen, 261191783Srmacklem newonep); 262191783Srmacklem 263191783Srmacklem /* 264191783Srmacklem * Serialize modifications to the open owner for multiple threads 265191783Srmacklem * within the same process using a read/write sleep lock. 266191783Srmacklem */ 267191783Srmacklem if (lockit) 268191783Srmacklem nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR); 269191783Srmacklem NFSUNLOCKCLSTATE(); 270191783Srmacklem if (nowp != NULL) 271191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 272191783Srmacklem if (nop != NULL) 273191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 274191783Srmacklem if (owpp != NULL) 275191783Srmacklem *owpp = owp; 276191783Srmacklem if (opp != NULL) 277191783Srmacklem *opp = op; 278206688Srmacklem if (retp != NULL) { 279206688Srmacklem if (nfhp != NULL && dp != NULL && nop == NULL) 280206688Srmacklem /* new local open on delegation */ 281206688Srmacklem *retp = NFSCLOPEN_SETCRED; 282206688Srmacklem else 283206688Srmacklem *retp = NFSCLOPEN_OK; 284206688Srmacklem } 285191783Srmacklem 286191783Srmacklem /* 287191783Srmacklem * Now, check the mode on the open and return the appropriate 288191783Srmacklem * value. 289191783Srmacklem */ 290191783Srmacklem if (op != NULL && (amode & ~(op->nfso_mode))) { 291191783Srmacklem op->nfso_mode |= amode; 292191783Srmacklem if (retp != NULL && dp == NULL) 293191783Srmacklem *retp = NFSCLOPEN_DOOPEN; 294191783Srmacklem } 295191783Srmacklem return (0); 296191783Srmacklem} 297191783Srmacklem 298191783Srmacklem/* 299191783Srmacklem * Create a new open, as required. 300191783Srmacklem */ 301191783Srmacklemstatic void 302191783Srmacklemnfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp, 303191783Srmacklem struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp, 304191783Srmacklem struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen, 305191783Srmacklem int *newonep) 306191783Srmacklem{ 307191783Srmacklem struct nfsclowner *owp = *owpp, *nowp; 308191783Srmacklem struct nfsclopen *op, *nop; 309191783Srmacklem 310191783Srmacklem if (nowpp != NULL) 311191783Srmacklem nowp = *nowpp; 312191783Srmacklem else 313191783Srmacklem nowp = NULL; 314191783Srmacklem if (nopp != NULL) 315191783Srmacklem nop = *nopp; 316191783Srmacklem else 317191783Srmacklem nop = NULL; 318191783Srmacklem if (owp == NULL && nowp != NULL) { 319191783Srmacklem NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN); 320191783Srmacklem LIST_INIT(&nowp->nfsow_open); 321191783Srmacklem nowp->nfsow_clp = clp; 322191783Srmacklem nowp->nfsow_seqid = 0; 323191783Srmacklem nowp->nfsow_defunct = 0; 324191783Srmacklem nfscl_lockinit(&nowp->nfsow_rwlock); 325191783Srmacklem if (dp != NULL) { 326191783Srmacklem newnfsstats.cllocalopenowners++; 327191783Srmacklem LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list); 328191783Srmacklem } else { 329191783Srmacklem newnfsstats.clopenowners++; 330191783Srmacklem LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list); 331191783Srmacklem } 332191783Srmacklem owp = *owpp = nowp; 333191783Srmacklem *nowpp = NULL; 334191783Srmacklem if (newonep != NULL) 335191783Srmacklem *newonep = 1; 336191783Srmacklem } 337191783Srmacklem 338191783Srmacklem /* If an fhp has been specified, create an Open as well. */ 339191783Srmacklem if (fhp != NULL) { 340191783Srmacklem /* and look for the correct open, based upon FH */ 341191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 342191783Srmacklem if (op->nfso_fhlen == fhlen && 343191783Srmacklem !NFSBCMP(op->nfso_fh, fhp, fhlen)) 344191783Srmacklem break; 345191783Srmacklem } 346191783Srmacklem if (op == NULL && nop != NULL) { 347191783Srmacklem nop->nfso_own = owp; 348191783Srmacklem nop->nfso_mode = 0; 349191783Srmacklem nop->nfso_opencnt = 0; 350191783Srmacklem nop->nfso_posixlock = 1; 351191783Srmacklem nop->nfso_fhlen = fhlen; 352191783Srmacklem NFSBCOPY(fhp, nop->nfso_fh, fhlen); 353191783Srmacklem LIST_INIT(&nop->nfso_lock); 354191783Srmacklem nop->nfso_stateid.seqid = 0; 355191783Srmacklem nop->nfso_stateid.other[0] = 0; 356191783Srmacklem nop->nfso_stateid.other[1] = 0; 357191783Srmacklem nop->nfso_stateid.other[2] = 0; 358191783Srmacklem if (dp != NULL) { 359191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 360191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, 361191783Srmacklem nfsdl_list); 362191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 363191783Srmacklem newnfsstats.cllocalopens++; 364191783Srmacklem } else { 365191783Srmacklem newnfsstats.clopens++; 366191783Srmacklem } 367191783Srmacklem LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list); 368191783Srmacklem *opp = nop; 369191783Srmacklem *nopp = NULL; 370191783Srmacklem if (newonep != NULL) 371191783Srmacklem *newonep = 1; 372191783Srmacklem } else { 373191783Srmacklem *opp = op; 374191783Srmacklem } 375191783Srmacklem } 376191783Srmacklem} 377191783Srmacklem 378191783Srmacklem/* 379191783Srmacklem * Called to find/add a delegation to a client. 380191783Srmacklem */ 381191783SrmacklemAPPLESTATIC int 382191783Srmacklemnfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp, 383191783Srmacklem int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp) 384191783Srmacklem{ 385191783Srmacklem struct nfscldeleg *dp = *dpp, *tdp; 386191783Srmacklem 387191783Srmacklem /* 388191783Srmacklem * First, if we have received a Read delegation for a file on a 389191783Srmacklem * read/write file system, just return it, because they aren't 390191783Srmacklem * useful, imho. 391191783Srmacklem */ 392191783Srmacklem if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) && 393191783Srmacklem (dp->nfsdl_flags & NFSCLDL_READ)) { 394191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p); 395191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 396191783Srmacklem *dpp = NULL; 397191783Srmacklem return (0); 398191783Srmacklem } 399191783Srmacklem 400191783Srmacklem /* Look for the correct deleg, based upon FH */ 401191783Srmacklem NFSLOCKCLSTATE(); 402191783Srmacklem tdp = nfscl_finddeleg(clp, nfhp, fhlen); 403191783Srmacklem if (tdp == NULL) { 404191783Srmacklem if (dp == NULL) { 405191783Srmacklem NFSUNLOCKCLSTATE(); 406191783Srmacklem return (NFSERR_BADSTATEID); 407191783Srmacklem } 408191783Srmacklem *dpp = NULL; 409191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list); 410191783Srmacklem LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp, 411191783Srmacklem nfsdl_hash); 412191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 413191783Srmacklem newnfsstats.cldelegates++; 414191783Srmacklem nfscl_delegcnt++; 415191783Srmacklem } else { 416191783Srmacklem /* 417191783Srmacklem * Delegation already exists, what do we do if a new one?? 418191783Srmacklem */ 419191783Srmacklem if (dp != NULL) { 420191783Srmacklem printf("Deleg already exists!\n"); 421191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 422191783Srmacklem *dpp = NULL; 423191783Srmacklem } else { 424191783Srmacklem *dpp = tdp; 425191783Srmacklem } 426191783Srmacklem } 427191783Srmacklem NFSUNLOCKCLSTATE(); 428191783Srmacklem return (0); 429191783Srmacklem} 430191783Srmacklem 431191783Srmacklem/* 432191783Srmacklem * Find a delegation for this file handle. Return NULL upon failure. 433191783Srmacklem */ 434191783Srmacklemstatic struct nfscldeleg * 435191783Srmacklemnfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen) 436191783Srmacklem{ 437191783Srmacklem struct nfscldeleg *dp; 438191783Srmacklem 439191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) { 440191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 441191783Srmacklem !NFSBCMP(dp->nfsdl_fh, fhp, fhlen)) 442191783Srmacklem break; 443191783Srmacklem } 444191783Srmacklem return (dp); 445191783Srmacklem} 446191783Srmacklem 447191783Srmacklem/* 448191783Srmacklem * Get a stateid for an I/O operation. First, look for an open and iff 449191783Srmacklem * found, return either a lockowner stateid or the open stateid. 450191783Srmacklem * If no Open is found, just return error and the special stateid of all zeros. 451191783Srmacklem */ 452191783SrmacklemAPPLESTATIC int 453191783Srmacklemnfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode, 454191783Srmacklem struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp, 455191783Srmacklem void **lckpp) 456191783Srmacklem{ 457191783Srmacklem struct nfsclclient *clp; 458191783Srmacklem struct nfsclowner *owp; 459195510Srmacklem struct nfsclopen *op = NULL; 460191783Srmacklem struct nfscllockowner *lp; 461191783Srmacklem struct nfscldeleg *dp; 462191783Srmacklem struct nfsnode *np; 463191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 464191783Srmacklem int error, done; 465191783Srmacklem 466191783Srmacklem *lckpp = NULL; 467191783Srmacklem /* 468191783Srmacklem * Initially, just set the special stateid of all zeros. 469191783Srmacklem */ 470191783Srmacklem stateidp->seqid = 0; 471191783Srmacklem stateidp->other[0] = 0; 472191783Srmacklem stateidp->other[1] = 0; 473191783Srmacklem stateidp->other[2] = 0; 474191783Srmacklem if (vnode_vtype(vp) != VREG) 475191783Srmacklem return (EISDIR); 476191783Srmacklem np = VTONFS(vp); 477191783Srmacklem NFSLOCKCLSTATE(); 478191783Srmacklem clp = nfscl_findcl(VFSTONFS(vnode_mount(vp))); 479191783Srmacklem if (clp == NULL) { 480191783Srmacklem NFSUNLOCKCLSTATE(); 481191783Srmacklem return (EACCES); 482191783Srmacklem } 483191783Srmacklem 484191783Srmacklem /* 485206818Srmacklem * Wait for recovery to complete. 486206818Srmacklem */ 487206818Srmacklem while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG)) 488206818Srmacklem (void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR, 489206818Srmacklem PZERO, "nfsrecvr", NULL); 490206818Srmacklem 491206818Srmacklem /* 492191783Srmacklem * First, look for a delegation. 493191783Srmacklem */ 494191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) { 495191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 496191783Srmacklem !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) { 497191783Srmacklem if (!(mode & NFSV4OPEN_ACCESSWRITE) || 498191783Srmacklem (dp->nfsdl_flags & NFSCLDL_WRITE)) { 499191783Srmacklem stateidp->seqid = dp->nfsdl_stateid.seqid; 500191783Srmacklem stateidp->other[0] = dp->nfsdl_stateid.other[0]; 501191783Srmacklem stateidp->other[1] = dp->nfsdl_stateid.other[1]; 502191783Srmacklem stateidp->other[2] = dp->nfsdl_stateid.other[2]; 503191783Srmacklem if (!(np->n_flag & NDELEGRECALL)) { 504191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, 505191783Srmacklem nfsdl_list); 506191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, 507191783Srmacklem nfsdl_list); 508191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 509191783Srmacklem 120; 510191783Srmacklem dp->nfsdl_rwlock.nfslock_usecnt++; 511191783Srmacklem *lckpp = (void *)&dp->nfsdl_rwlock; 512191783Srmacklem } 513191783Srmacklem NFSUNLOCKCLSTATE(); 514191783Srmacklem return (0); 515191783Srmacklem } 516191783Srmacklem break; 517191783Srmacklem } 518191783Srmacklem } 519191783Srmacklem 520191783Srmacklem if (p != NULL) { 521191783Srmacklem /* 522191783Srmacklem * If p != NULL, we want to search the parentage tree 523191783Srmacklem * for a matching OpenOwner and use that. 524191783Srmacklem */ 525222719Srmacklem nfscl_filllockowner(p->td_proc, own, F_POSIX); 526223774Srmacklem lp = NULL; 527223774Srmacklem error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, own, own, 528223774Srmacklem mode, &lp, &op); 529223774Srmacklem if (error == 0 && lp != NULL) { 530223774Srmacklem stateidp->seqid = 531223774Srmacklem lp->nfsl_stateid.seqid; 532223774Srmacklem stateidp->other[0] = 533223774Srmacklem lp->nfsl_stateid.other[0]; 534223774Srmacklem stateidp->other[1] = 535223774Srmacklem lp->nfsl_stateid.other[1]; 536223774Srmacklem stateidp->other[2] = 537223774Srmacklem lp->nfsl_stateid.other[2]; 538223774Srmacklem NFSUNLOCKCLSTATE(); 539223774Srmacklem return (0); 540191783Srmacklem } 541195510Srmacklem } 542195510Srmacklem if (op == NULL) { 543195510Srmacklem /* If not found, just look for any OpenOwner that will work. */ 544191783Srmacklem done = 0; 545191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 546191783Srmacklem while (!done && owp != NULL) { 547195510Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 548195510Srmacklem if (op->nfso_fhlen == fhlen && 549195510Srmacklem !NFSBCMP(op->nfso_fh, nfhp, fhlen) && 550195510Srmacklem (mode & op->nfso_mode) == mode) { 551195510Srmacklem done = 1; 552195510Srmacklem break; 553195510Srmacklem } 554191783Srmacklem } 555195510Srmacklem if (!done) 556195510Srmacklem owp = LIST_NEXT(owp, nfsow_list); 557191783Srmacklem } 558191783Srmacklem if (!done) { 559191783Srmacklem NFSUNLOCKCLSTATE(); 560191783Srmacklem return (ENOENT); 561191783Srmacklem } 562231545Srmacklem /* 563231545Srmacklem * For read aheads or write behinds, use the open cred. 564231545Srmacklem * A read ahead or write behind is indicated by p == NULL. 565231545Srmacklem */ 566231545Srmacklem if (p == NULL) 567231545Srmacklem newnfs_copycred(&op->nfso_cred, cred); 568191783Srmacklem } 569191783Srmacklem 570191783Srmacklem /* 571191783Srmacklem * No lock stateid, so return the open stateid. 572191783Srmacklem */ 573191783Srmacklem stateidp->seqid = op->nfso_stateid.seqid; 574191783Srmacklem stateidp->other[0] = op->nfso_stateid.other[0]; 575191783Srmacklem stateidp->other[1] = op->nfso_stateid.other[1]; 576191783Srmacklem stateidp->other[2] = op->nfso_stateid.other[2]; 577191783Srmacklem NFSUNLOCKCLSTATE(); 578191783Srmacklem return (0); 579191783Srmacklem} 580191783Srmacklem 581191783Srmacklem/* 582223774Srmacklem * Search for a matching file, mode and, optionally, lockowner. 583191783Srmacklem */ 584191783Srmacklemstatic int 585191783Srmacklemnfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen, 586223774Srmacklem u_int8_t *openown, u_int8_t *lockown, u_int32_t mode, 587223774Srmacklem struct nfscllockowner **lpp, struct nfsclopen **opp) 588191783Srmacklem{ 589223774Srmacklem struct nfsclowner *owp; 590223774Srmacklem struct nfsclopen *op, *rop, *rop2; 591223774Srmacklem struct nfscllockowner *lp; 592223774Srmacklem int keep_looping; 593191783Srmacklem 594223774Srmacklem if (lpp != NULL) 595223774Srmacklem *lpp = NULL; 596223774Srmacklem /* 597223774Srmacklem * rop will be set to the open to be returned. There are three 598223774Srmacklem * variants of this, all for an open of the correct file: 599223774Srmacklem * 1 - A match of lockown. 600223774Srmacklem * 2 - A match of the openown, when no lockown match exists. 601223774Srmacklem * 3 - A match for any open, if no openown or lockown match exists. 602223774Srmacklem * Looking for #2 over #3 probably isn't necessary, but since 603223774Srmacklem * RFC3530 is vague w.r.t. the relationship between openowners and 604223774Srmacklem * lockowners, I think this is the safer way to go. 605223774Srmacklem */ 606223774Srmacklem rop = NULL; 607223774Srmacklem rop2 = NULL; 608223774Srmacklem keep_looping = 1; 609223774Srmacklem /* Search the client list */ 610223774Srmacklem owp = LIST_FIRST(ohp); 611223774Srmacklem while (owp != NULL && keep_looping != 0) { 612223774Srmacklem /* and look for the correct open */ 613223774Srmacklem op = LIST_FIRST(&owp->nfsow_open); 614223774Srmacklem while (op != NULL && keep_looping != 0) { 615223774Srmacklem if (op->nfso_fhlen == fhlen && 616223774Srmacklem !NFSBCMP(op->nfso_fh, nfhp, fhlen) 617223774Srmacklem && (op->nfso_mode & mode) == mode) { 618223774Srmacklem if (lpp != NULL) { 619223774Srmacklem /* Now look for a matching lockowner. */ 620223774Srmacklem LIST_FOREACH(lp, &op->nfso_lock, 621223774Srmacklem nfsl_list) { 622223774Srmacklem if (!NFSBCMP(lp->nfsl_owner, 623223774Srmacklem lockown, 624223774Srmacklem NFSV4CL_LOCKNAMELEN)) { 625223774Srmacklem *lpp = lp; 626223774Srmacklem rop = op; 627223774Srmacklem keep_looping = 0; 628223774Srmacklem break; 629223774Srmacklem } 630223774Srmacklem } 631191783Srmacklem } 632223774Srmacklem if (rop == NULL && !NFSBCMP(owp->nfsow_owner, 633223774Srmacklem openown, NFSV4CL_LOCKNAMELEN)) { 634223774Srmacklem rop = op; 635223774Srmacklem if (lpp == NULL) 636223774Srmacklem keep_looping = 0; 637223774Srmacklem } 638223774Srmacklem if (rop2 == NULL) 639223774Srmacklem rop2 = op; 640191783Srmacklem } 641223774Srmacklem op = LIST_NEXT(op, nfso_list); 642191783Srmacklem } 643223774Srmacklem owp = LIST_NEXT(owp, nfsow_list); 644191783Srmacklem } 645223774Srmacklem if (rop == NULL) 646223774Srmacklem rop = rop2; 647223774Srmacklem if (rop == NULL) 648191783Srmacklem return (EBADF); 649223774Srmacklem *opp = rop; 650191783Srmacklem return (0); 651191783Srmacklem} 652191783Srmacklem 653191783Srmacklem/* 654191783Srmacklem * Release use of an open owner. Called when open operations are done 655191783Srmacklem * with the open owner. 656191783Srmacklem */ 657191783SrmacklemAPPLESTATIC void 658191783Srmacklemnfscl_ownerrelease(struct nfsclowner *owp, __unused int error, 659191783Srmacklem __unused int candelete, int unlocked) 660191783Srmacklem{ 661191783Srmacklem 662191783Srmacklem if (owp == NULL) 663191783Srmacklem return; 664191783Srmacklem NFSLOCKCLSTATE(); 665191783Srmacklem if (!unlocked) 666191783Srmacklem nfscl_lockunlock(&owp->nfsow_rwlock); 667191783Srmacklem nfscl_clrelease(owp->nfsow_clp); 668191783Srmacklem NFSUNLOCKCLSTATE(); 669191783Srmacklem} 670191783Srmacklem 671191783Srmacklem/* 672191783Srmacklem * Release use of an open structure under an open owner. 673191783Srmacklem */ 674191783SrmacklemAPPLESTATIC void 675191783Srmacklemnfscl_openrelease(struct nfsclopen *op, int error, int candelete) 676191783Srmacklem{ 677191783Srmacklem struct nfsclclient *clp; 678191783Srmacklem struct nfsclowner *owp; 679191783Srmacklem 680191783Srmacklem if (op == NULL) 681191783Srmacklem return; 682191783Srmacklem NFSLOCKCLSTATE(); 683191783Srmacklem owp = op->nfso_own; 684191783Srmacklem nfscl_lockunlock(&owp->nfsow_rwlock); 685191783Srmacklem clp = owp->nfsow_clp; 686191783Srmacklem if (error && candelete && op->nfso_opencnt == 0) 687191783Srmacklem nfscl_freeopen(op, 0); 688191783Srmacklem nfscl_clrelease(clp); 689191783Srmacklem NFSUNLOCKCLSTATE(); 690191783Srmacklem} 691191783Srmacklem 692191783Srmacklem/* 693191783Srmacklem * Called to get a clientid structure. It will optionally lock the 694191783Srmacklem * client data structures to do the SetClientId/SetClientId_confirm, 695191783Srmacklem * but will release that lock and return the clientid with a refernce 696191783Srmacklem * count on it. 697193735Srmacklem * If the "cred" argument is NULL, a new clientid should not be created. 698193735Srmacklem * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot 699193735Srmacklem * be done. 700192337Srmacklem * It always clpp with a reference count on it, unless returning an error. 701191783Srmacklem */ 702191783SrmacklemAPPLESTATIC int 703191783Srmacklemnfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, 704191783Srmacklem struct nfsclclient **clpp) 705191783Srmacklem{ 706191783Srmacklem struct nfsclclient *clp; 707193735Srmacklem struct nfsclclient *newclp = NULL; 708222389Srmacklem struct mount *mp; 709222389Srmacklem struct nfsmount *nmp; 710193066Sjamie char uuid[HOSTUUIDLEN]; 711191783Srmacklem int igotlock = 0, error, trystalecnt, clidinusedelay, i; 712193735Srmacklem u_int16_t idlen = 0; 713191783Srmacklem 714222389Srmacklem mp = vnode_mount(vp); 715222389Srmacklem nmp = VFSTONFS(mp); 716193735Srmacklem if (cred != NULL) { 717194117Sjamie getcredhostuuid(cred, uuid, sizeof uuid); 718193735Srmacklem idlen = strlen(uuid); 719193735Srmacklem if (idlen > 0) 720193735Srmacklem idlen += sizeof (u_int64_t); 721193735Srmacklem else 722193735Srmacklem idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */ 723193735Srmacklem MALLOC(newclp, struct nfsclclient *, 724193735Srmacklem sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT, 725193735Srmacklem M_WAITOK); 726193735Srmacklem } 727191783Srmacklem NFSLOCKCLSTATE(); 728222389Srmacklem /* 729222389Srmacklem * If a forced dismount is already in progress, don't 730222389Srmacklem * allocate a new clientid and get out now. For the case where 731222389Srmacklem * clp != NULL, this is a harmless optimization. 732222389Srmacklem */ 733222389Srmacklem if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 734222389Srmacklem NFSUNLOCKCLSTATE(); 735222389Srmacklem if (newclp != NULL) 736222389Srmacklem free(newclp, M_NFSCLCLIENT); 737222389Srmacklem return (EBADF); 738222389Srmacklem } 739191783Srmacklem clp = nmp->nm_clp; 740191783Srmacklem if (clp == NULL) { 741193735Srmacklem if (newclp == NULL) { 742193735Srmacklem NFSUNLOCKCLSTATE(); 743193735Srmacklem return (EACCES); 744193735Srmacklem } 745191783Srmacklem clp = newclp; 746191783Srmacklem NFSBZERO((caddr_t)clp, sizeof(struct nfsclclient) + idlen - 1); 747191783Srmacklem clp->nfsc_idlen = idlen; 748191783Srmacklem LIST_INIT(&clp->nfsc_owner); 749191783Srmacklem TAILQ_INIT(&clp->nfsc_deleg); 750191783Srmacklem for (i = 0; i < NFSCLDELEGHASHSIZE; i++) 751191783Srmacklem LIST_INIT(&clp->nfsc_deleghash[i]); 752191783Srmacklem clp->nfsc_flags = NFSCLFLAGS_INITED; 753191783Srmacklem clp->nfsc_clientidrev = 1; 754191783Srmacklem clp->nfsc_cbident = nfscl_nextcbident(); 755193066Sjamie nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id, 756191783Srmacklem clp->nfsc_idlen); 757191783Srmacklem LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list); 758191783Srmacklem nmp->nm_clp = clp; 759191783Srmacklem clp->nfsc_nmp = nmp; 760191783Srmacklem NFSUNLOCKCLSTATE(); 761191783Srmacklem nfscl_start_renewthread(clp); 762191783Srmacklem } else { 763191783Srmacklem NFSUNLOCKCLSTATE(); 764193735Srmacklem if (newclp != NULL) 765193735Srmacklem FREE((caddr_t)newclp, M_NFSCLCLIENT); 766191783Srmacklem } 767191783Srmacklem NFSLOCKCLSTATE(); 768223971Srmacklem while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock && 769223971Srmacklem (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0) 770191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 771222389Srmacklem NFSCLSTATEMUTEXPTR, mp); 772191783Srmacklem if (!igotlock) 773222389Srmacklem nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp); 774222389Srmacklem if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 775222389Srmacklem /* 776222389Srmacklem * Both nfsv4_lock() and nfsv4_getref() know to check 777222389Srmacklem * for MNTK_UNMOUNTF and return without sleeping to 778222389Srmacklem * wait for the exclusive lock to be released, since it 779222389Srmacklem * might be held by nfscl_umount() and we need to get out 780222389Srmacklem * now for that case and not wait until nfscl_umount() 781222389Srmacklem * releases it. 782222389Srmacklem */ 783222389Srmacklem NFSUNLOCKCLSTATE(); 784222389Srmacklem return (EBADF); 785222389Srmacklem } 786191783Srmacklem NFSUNLOCKCLSTATE(); 787191783Srmacklem 788191783Srmacklem /* 789191783Srmacklem * If it needs a clientid, do the setclientid now. 790191783Srmacklem */ 791191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) { 792191783Srmacklem if (!igotlock) 793191783Srmacklem panic("nfscl_clget"); 794193735Srmacklem if (p == NULL || cred == NULL) { 795191783Srmacklem NFSLOCKCLSTATE(); 796191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 797191783Srmacklem NFSUNLOCKCLSTATE(); 798191783Srmacklem return (EACCES); 799191783Srmacklem } 800191783Srmacklem /* 801191783Srmacklem * If RFC3530 Sec. 14.2.33 is taken literally, 802191783Srmacklem * NFSERR_CLIDINUSE will be returned persistently for the 803191783Srmacklem * case where a new mount of the same file system is using 804191783Srmacklem * a different principal. In practice, NFSERR_CLIDINUSE is 805191783Srmacklem * only returned when there is outstanding unexpired state 806191783Srmacklem * on the clientid. As such, try for twice the lease 807191783Srmacklem * interval, if we know what that is. Otherwise, make a 808191783Srmacklem * wild ass guess. 809191783Srmacklem * The case of returning NFSERR_STALECLIENTID is far less 810191783Srmacklem * likely, but might occur if there is a significant delay 811191783Srmacklem * between doing the SetClientID and SetClientIDConfirm Ops, 812191783Srmacklem * such that the server throws away the clientid before 813191783Srmacklem * receiving the SetClientIDConfirm. 814191783Srmacklem */ 815191783Srmacklem if (clp->nfsc_renew > 0) 816191783Srmacklem clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2; 817191783Srmacklem else 818191783Srmacklem clidinusedelay = 120; 819191783Srmacklem trystalecnt = 3; 820191783Srmacklem do { 821193735Srmacklem error = nfsrpc_setclient(VFSTONFS(vnode_mount(vp)), 822193735Srmacklem clp, cred, p); 823191783Srmacklem if (error == NFSERR_STALECLIENTID || 824191783Srmacklem error == NFSERR_STALEDONTRECOVER || 825191783Srmacklem error == NFSERR_CLIDINUSE) { 826207170Srmacklem (void) nfs_catnap(PZERO, error, "nfs_setcl"); 827191783Srmacklem } 828191783Srmacklem } while (((error == NFSERR_STALECLIENTID || 829191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) || 830191783Srmacklem (error == NFSERR_CLIDINUSE && --clidinusedelay > 0)); 831191783Srmacklem if (error) { 832191783Srmacklem NFSLOCKCLSTATE(); 833191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 834191783Srmacklem NFSUNLOCKCLSTATE(); 835191783Srmacklem return (error); 836191783Srmacklem } 837191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 838191783Srmacklem } 839191783Srmacklem if (igotlock) { 840191783Srmacklem NFSLOCKCLSTATE(); 841191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 1); 842191783Srmacklem NFSUNLOCKCLSTATE(); 843191783Srmacklem } 844191783Srmacklem 845191783Srmacklem *clpp = clp; 846191783Srmacklem return (0); 847191783Srmacklem} 848191783Srmacklem 849191783Srmacklem/* 850191783Srmacklem * Get a reference to a clientid and return it, if valid. 851191783Srmacklem */ 852191783SrmacklemAPPLESTATIC struct nfsclclient * 853191783Srmacklemnfscl_findcl(struct nfsmount *nmp) 854191783Srmacklem{ 855191783Srmacklem struct nfsclclient *clp; 856191783Srmacklem 857191783Srmacklem clp = nmp->nm_clp; 858191783Srmacklem if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) 859191783Srmacklem return (NULL); 860191783Srmacklem return (clp); 861191783Srmacklem} 862191783Srmacklem 863191783Srmacklem/* 864191783Srmacklem * Release the clientid structure. It may be locked or reference counted. 865191783Srmacklem */ 866191783Srmacklemstatic void 867191783Srmacklemnfscl_clrelease(struct nfsclclient *clp) 868191783Srmacklem{ 869191783Srmacklem 870191783Srmacklem if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK) 871191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 872191783Srmacklem else 873191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 874191783Srmacklem} 875191783Srmacklem 876191783Srmacklem/* 877191783Srmacklem * External call for nfscl_clrelease. 878191783Srmacklem */ 879191783SrmacklemAPPLESTATIC void 880191783Srmacklemnfscl_clientrelease(struct nfsclclient *clp) 881191783Srmacklem{ 882191783Srmacklem 883191783Srmacklem NFSLOCKCLSTATE(); 884191783Srmacklem if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK) 885191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 886191783Srmacklem else 887191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 888191783Srmacklem NFSUNLOCKCLSTATE(); 889191783Srmacklem} 890191783Srmacklem 891191783Srmacklem/* 892191783Srmacklem * Called when wanting to lock a byte region. 893191783Srmacklem */ 894191783SrmacklemAPPLESTATIC int 895191783Srmacklemnfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len, 896191783Srmacklem short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp, 897222719Srmacklem int recovery, void *id, int flags, u_int8_t *rownp, u_int8_t *ropenownp, 898191783Srmacklem struct nfscllockowner **lpp, int *newonep, int *donelocallyp) 899191783Srmacklem{ 900191783Srmacklem struct nfscllockowner *lp; 901191783Srmacklem struct nfsclopen *op; 902191783Srmacklem struct nfsclclient *clp; 903191783Srmacklem struct nfscllockowner *nlp; 904191783Srmacklem struct nfscllock *nlop, *otherlop; 905191783Srmacklem struct nfscldeleg *dp = NULL, *ldp = NULL; 906191783Srmacklem struct nfscllockownerhead *lhp = NULL; 907191783Srmacklem struct nfsnode *np; 908223774Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp, openown[NFSV4CL_LOCKNAMELEN]; 909223774Srmacklem u_int8_t *openownp; 910191783Srmacklem int error = 0, ret, donelocally = 0; 911191783Srmacklem u_int32_t mode; 912191783Srmacklem 913223774Srmacklem /* For Lock Ops, the open mode doesn't matter, so use 0 to match any. */ 914223774Srmacklem mode = 0; 915191783Srmacklem np = VTONFS(vp); 916191783Srmacklem *lpp = NULL; 917223774Srmacklem lp = NULL; 918191783Srmacklem *newonep = 0; 919191783Srmacklem *donelocallyp = 0; 920191783Srmacklem 921191783Srmacklem /* 922191783Srmacklem * Might need these, so MALLOC them now, to 923191783Srmacklem * avoid a tsleep() in MALLOC later. 924191783Srmacklem */ 925191783Srmacklem MALLOC(nlp, struct nfscllockowner *, 926191783Srmacklem sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK); 927191783Srmacklem MALLOC(otherlop, struct nfscllock *, 928191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 929191783Srmacklem MALLOC(nlop, struct nfscllock *, 930191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 931191783Srmacklem nlop->nfslo_type = type; 932191783Srmacklem nlop->nfslo_first = off; 933191783Srmacklem if (len == NFS64BITSSET) { 934191783Srmacklem nlop->nfslo_end = NFS64BITSSET; 935191783Srmacklem } else { 936191783Srmacklem nlop->nfslo_end = off + len; 937191783Srmacklem if (nlop->nfslo_end <= nlop->nfslo_first) 938191783Srmacklem error = NFSERR_INVAL; 939191783Srmacklem } 940191783Srmacklem 941191783Srmacklem if (!error) { 942191783Srmacklem if (recovery) 943191783Srmacklem clp = rclp; 944191783Srmacklem else 945191783Srmacklem error = nfscl_getcl(vp, cred, p, &clp); 946191783Srmacklem } 947191783Srmacklem if (error) { 948191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 949191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 950191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 951191783Srmacklem return (error); 952191783Srmacklem } 953191783Srmacklem 954191783Srmacklem op = NULL; 955191783Srmacklem if (recovery) { 956191783Srmacklem ownp = rownp; 957223774Srmacklem openownp = ropenownp; 958191783Srmacklem } else { 959222719Srmacklem nfscl_filllockowner(id, own, flags); 960191783Srmacklem ownp = own; 961223774Srmacklem nfscl_filllockowner(p->td_proc, openown, F_POSIX); 962223774Srmacklem openownp = openown; 963191783Srmacklem } 964191783Srmacklem if (!recovery) { 965191783Srmacklem NFSLOCKCLSTATE(); 966191783Srmacklem /* 967191783Srmacklem * First, search for a delegation. If one exists for this file, 968191783Srmacklem * the lock can be done locally against it, so long as there 969191783Srmacklem * isn't a local lock conflict. 970191783Srmacklem */ 971191783Srmacklem ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 972191783Srmacklem np->n_fhp->nfh_len); 973191783Srmacklem /* Just sanity check for correct type of delegation */ 974214406Srmacklem if (dp != NULL && ((dp->nfsdl_flags & 975214406Srmacklem (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 || 976214406Srmacklem (type == F_WRLCK && 977214406Srmacklem (dp->nfsdl_flags & NFSCLDL_WRITE) == 0))) 978191783Srmacklem dp = NULL; 979191783Srmacklem } 980191783Srmacklem if (dp != NULL) { 981223774Srmacklem /* Now, find an open and maybe a lockowner. */ 982191783Srmacklem ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh, 983223774Srmacklem np->n_fhp->nfh_len, openownp, ownp, mode, NULL, &op); 984191783Srmacklem if (ret) 985191783Srmacklem ret = nfscl_getopen(&clp->nfsc_owner, 986223774Srmacklem np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp, 987223774Srmacklem ownp, mode, NULL, &op); 988191783Srmacklem if (!ret) { 989191783Srmacklem lhp = &dp->nfsdl_lock; 990191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 991191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list); 992191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 993191783Srmacklem donelocally = 1; 994191783Srmacklem } else { 995191783Srmacklem dp = NULL; 996191783Srmacklem } 997191783Srmacklem } 998191783Srmacklem if (!donelocally) { 999191783Srmacklem /* 1000223774Srmacklem * Get the related Open and maybe lockowner. 1001191783Srmacklem */ 1002223774Srmacklem error = nfscl_getopen(&clp->nfsc_owner, 1003223774Srmacklem np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp, 1004223774Srmacklem ownp, mode, &lp, &op); 1005191783Srmacklem if (!error) 1006191783Srmacklem lhp = &op->nfso_lock; 1007191783Srmacklem } 1008191783Srmacklem if (!error && !recovery) 1009201439Srmacklem error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, 1010201439Srmacklem np->n_fhp->nfh_len, nlop, ownp, ldp, NULL); 1011191783Srmacklem if (error) { 1012191783Srmacklem if (!recovery) { 1013191783Srmacklem nfscl_clrelease(clp); 1014191783Srmacklem NFSUNLOCKCLSTATE(); 1015191783Srmacklem } 1016191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 1017191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 1018191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1019191783Srmacklem return (error); 1020191783Srmacklem } 1021191783Srmacklem 1022191783Srmacklem /* 1023191783Srmacklem * Ok, see if a lockowner exists and create one, as required. 1024191783Srmacklem */ 1025223774Srmacklem if (lp == NULL) 1026223774Srmacklem LIST_FOREACH(lp, lhp, nfsl_list) { 1027223774Srmacklem if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN)) 1028223774Srmacklem break; 1029223774Srmacklem } 1030191783Srmacklem if (lp == NULL) { 1031191783Srmacklem NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN); 1032191783Srmacklem if (recovery) 1033191783Srmacklem NFSBCOPY(ropenownp, nlp->nfsl_openowner, 1034191783Srmacklem NFSV4CL_LOCKNAMELEN); 1035191783Srmacklem else 1036191783Srmacklem NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner, 1037191783Srmacklem NFSV4CL_LOCKNAMELEN); 1038191783Srmacklem nlp->nfsl_seqid = 0; 1039229752Srmacklem nlp->nfsl_lockflags = flags; 1040191783Srmacklem nlp->nfsl_inprog = NULL; 1041191783Srmacklem nfscl_lockinit(&nlp->nfsl_rwlock); 1042191783Srmacklem LIST_INIT(&nlp->nfsl_lock); 1043191783Srmacklem if (donelocally) { 1044191783Srmacklem nlp->nfsl_open = NULL; 1045191783Srmacklem newnfsstats.cllocallockowners++; 1046191783Srmacklem } else { 1047191783Srmacklem nlp->nfsl_open = op; 1048191783Srmacklem newnfsstats.cllockowners++; 1049191783Srmacklem } 1050191783Srmacklem LIST_INSERT_HEAD(lhp, nlp, nfsl_list); 1051191783Srmacklem lp = nlp; 1052191783Srmacklem nlp = NULL; 1053191783Srmacklem *newonep = 1; 1054191783Srmacklem } 1055191783Srmacklem 1056191783Srmacklem /* 1057191783Srmacklem * Now, update the byte ranges for locks. 1058191783Srmacklem */ 1059191783Srmacklem ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally); 1060191783Srmacklem if (!ret) 1061191783Srmacklem donelocally = 1; 1062191783Srmacklem if (donelocally) { 1063191783Srmacklem *donelocallyp = 1; 1064191783Srmacklem if (!recovery) 1065191783Srmacklem nfscl_clrelease(clp); 1066191783Srmacklem } else { 1067191783Srmacklem /* 1068191783Srmacklem * Serial modifications on the lock owner for multiple threads 1069191783Srmacklem * for the same process using a read/write lock. 1070191783Srmacklem */ 1071191783Srmacklem if (!recovery) 1072191783Srmacklem nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR); 1073191783Srmacklem } 1074191783Srmacklem if (!recovery) 1075191783Srmacklem NFSUNLOCKCLSTATE(); 1076191783Srmacklem 1077191783Srmacklem if (nlp) 1078191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 1079191783Srmacklem if (nlop) 1080191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1081191783Srmacklem if (otherlop) 1082191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 1083191783Srmacklem 1084191783Srmacklem *lpp = lp; 1085191783Srmacklem return (0); 1086191783Srmacklem} 1087191783Srmacklem 1088191783Srmacklem/* 1089191783Srmacklem * Called to unlock a byte range, for LockU. 1090191783Srmacklem */ 1091191783SrmacklemAPPLESTATIC int 1092191783Srmacklemnfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len, 1093191783Srmacklem __unused struct ucred *cred, NFSPROC_T *p, int callcnt, 1094222719Srmacklem struct nfsclclient *clp, void *id, int flags, 1095222719Srmacklem struct nfscllockowner **lpp, int *dorpcp) 1096191783Srmacklem{ 1097191783Srmacklem struct nfscllockowner *lp; 1098191783Srmacklem struct nfsclowner *owp; 1099191783Srmacklem struct nfsclopen *op; 1100191783Srmacklem struct nfscllock *nlop, *other_lop = NULL; 1101191783Srmacklem struct nfscldeleg *dp; 1102191783Srmacklem struct nfsnode *np; 1103191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1104201439Srmacklem int ret = 0, fnd; 1105191783Srmacklem 1106191783Srmacklem np = VTONFS(vp); 1107191783Srmacklem *lpp = NULL; 1108191783Srmacklem *dorpcp = 0; 1109191783Srmacklem 1110191783Srmacklem /* 1111191783Srmacklem * Might need these, so MALLOC them now, to 1112191783Srmacklem * avoid a tsleep() in MALLOC later. 1113191783Srmacklem */ 1114191783Srmacklem MALLOC(nlop, struct nfscllock *, 1115191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 1116191783Srmacklem nlop->nfslo_type = F_UNLCK; 1117191783Srmacklem nlop->nfslo_first = off; 1118191783Srmacklem if (len == NFS64BITSSET) { 1119191783Srmacklem nlop->nfslo_end = NFS64BITSSET; 1120191783Srmacklem } else { 1121191783Srmacklem nlop->nfslo_end = off + len; 1122191783Srmacklem if (nlop->nfslo_end <= nlop->nfslo_first) { 1123191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1124191783Srmacklem return (NFSERR_INVAL); 1125191783Srmacklem } 1126191783Srmacklem } 1127191783Srmacklem if (callcnt == 0) { 1128191783Srmacklem MALLOC(other_lop, struct nfscllock *, 1129191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 1130191783Srmacklem *other_lop = *nlop; 1131191783Srmacklem } 1132222719Srmacklem nfscl_filllockowner(id, own, flags); 1133191783Srmacklem dp = NULL; 1134191783Srmacklem NFSLOCKCLSTATE(); 1135191783Srmacklem if (callcnt == 0) 1136191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 1137191783Srmacklem np->n_fhp->nfh_len); 1138191783Srmacklem 1139191783Srmacklem /* 1140191783Srmacklem * First, unlock any local regions on a delegation. 1141191783Srmacklem */ 1142191783Srmacklem if (dp != NULL) { 1143191783Srmacklem /* Look for this lockowner. */ 1144191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 1145191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1146191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1147191783Srmacklem break; 1148191783Srmacklem } 1149191783Srmacklem if (lp != NULL) 1150191783Srmacklem /* Use other_lop, so nlop is still available */ 1151191783Srmacklem (void)nfscl_updatelock(lp, &other_lop, NULL, 1); 1152191783Srmacklem } 1153191783Srmacklem 1154191783Srmacklem /* 1155191783Srmacklem * Now, find a matching open/lockowner that hasn't already been done, 1156191783Srmacklem * as marked by nfsl_inprog. 1157191783Srmacklem */ 1158191783Srmacklem lp = NULL; 1159191783Srmacklem fnd = 0; 1160191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1161191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1162191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1163191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1164191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1165191783Srmacklem if (lp->nfsl_inprog == NULL && 1166191783Srmacklem !NFSBCMP(lp->nfsl_owner, own, 1167191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1168191783Srmacklem fnd = 1; 1169191783Srmacklem break; 1170191783Srmacklem } 1171191783Srmacklem } 1172191783Srmacklem if (fnd) 1173191783Srmacklem break; 1174191783Srmacklem } 1175191783Srmacklem } 1176191783Srmacklem if (fnd) 1177191783Srmacklem break; 1178191783Srmacklem } 1179191783Srmacklem 1180191783Srmacklem if (lp != NULL) { 1181191783Srmacklem ret = nfscl_updatelock(lp, &nlop, NULL, 0); 1182191783Srmacklem if (ret) 1183191783Srmacklem *dorpcp = 1; 1184191783Srmacklem /* 1185191783Srmacklem * Serial modifications on the lock owner for multiple 1186191783Srmacklem * threads for the same process using a read/write lock. 1187191783Srmacklem */ 1188191783Srmacklem lp->nfsl_inprog = p; 1189191783Srmacklem nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR); 1190191783Srmacklem *lpp = lp; 1191191783Srmacklem } 1192191783Srmacklem NFSUNLOCKCLSTATE(); 1193191783Srmacklem if (nlop) 1194191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1195191783Srmacklem if (other_lop) 1196191783Srmacklem FREE((caddr_t)other_lop, M_NFSCLLOCK); 1197191783Srmacklem return (0); 1198191783Srmacklem} 1199191783Srmacklem 1200191783Srmacklem/* 1201191783Srmacklem * Release all lockowners marked in progess for this process and file. 1202191783Srmacklem */ 1203191783SrmacklemAPPLESTATIC void 1204222719Srmacklemnfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p, 1205222719Srmacklem void *id, int flags) 1206191783Srmacklem{ 1207191783Srmacklem struct nfsclowner *owp; 1208191783Srmacklem struct nfsclopen *op; 1209191783Srmacklem struct nfscllockowner *lp; 1210191783Srmacklem struct nfsnode *np; 1211191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1212191783Srmacklem 1213191783Srmacklem np = VTONFS(vp); 1214222719Srmacklem nfscl_filllockowner(id, own, flags); 1215191783Srmacklem NFSLOCKCLSTATE(); 1216191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1217191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1218191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1219191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1220191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1221191783Srmacklem if (lp->nfsl_inprog == p && 1222191783Srmacklem !NFSBCMP(lp->nfsl_owner, own, 1223191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1224191783Srmacklem lp->nfsl_inprog = NULL; 1225191783Srmacklem nfscl_lockunlock(&lp->nfsl_rwlock); 1226191783Srmacklem } 1227191783Srmacklem } 1228191783Srmacklem } 1229191783Srmacklem } 1230191783Srmacklem } 1231191783Srmacklem nfscl_clrelease(clp); 1232191783Srmacklem NFSUNLOCKCLSTATE(); 1233191783Srmacklem} 1234191783Srmacklem 1235191783Srmacklem/* 1236191783Srmacklem * Called to find out if any bytes within the byte range specified are 1237191783Srmacklem * write locked by the calling process. Used to determine if flushing 1238191783Srmacklem * is required before a LockU. 1239191783Srmacklem * If in doubt, return 1, so the flush will occur. 1240191783Srmacklem */ 1241191783SrmacklemAPPLESTATIC int 1242191783Srmacklemnfscl_checkwritelocked(vnode_t vp, struct flock *fl, 1243222719Srmacklem struct ucred *cred, NFSPROC_T *p, void *id, int flags) 1244191783Srmacklem{ 1245191783Srmacklem struct nfsclowner *owp; 1246191783Srmacklem struct nfscllockowner *lp; 1247191783Srmacklem struct nfsclopen *op; 1248191783Srmacklem struct nfsclclient *clp; 1249191783Srmacklem struct nfscllock *lop; 1250191783Srmacklem struct nfscldeleg *dp; 1251191783Srmacklem struct nfsnode *np; 1252191783Srmacklem u_int64_t off, end; 1253191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1254191783Srmacklem int error = 0; 1255191783Srmacklem 1256191783Srmacklem np = VTONFS(vp); 1257191783Srmacklem switch (fl->l_whence) { 1258191783Srmacklem case SEEK_SET: 1259191783Srmacklem case SEEK_CUR: 1260191783Srmacklem /* 1261191783Srmacklem * Caller is responsible for adding any necessary offset 1262191783Srmacklem * when SEEK_CUR is used. 1263191783Srmacklem */ 1264191783Srmacklem off = fl->l_start; 1265191783Srmacklem break; 1266191783Srmacklem case SEEK_END: 1267191783Srmacklem off = np->n_size + fl->l_start; 1268191783Srmacklem break; 1269191783Srmacklem default: 1270191783Srmacklem return (1); 1271191783Srmacklem }; 1272191783Srmacklem if (fl->l_len != 0) { 1273191783Srmacklem end = off + fl->l_len; 1274191783Srmacklem if (end < off) 1275191783Srmacklem return (1); 1276191783Srmacklem } else { 1277191783Srmacklem end = NFS64BITSSET; 1278191783Srmacklem } 1279191783Srmacklem 1280191783Srmacklem error = nfscl_getcl(vp, cred, p, &clp); 1281191783Srmacklem if (error) 1282191783Srmacklem return (1); 1283222719Srmacklem nfscl_filllockowner(id, own, flags); 1284191783Srmacklem NFSLOCKCLSTATE(); 1285191783Srmacklem 1286191783Srmacklem /* 1287191783Srmacklem * First check the delegation locks. 1288191783Srmacklem */ 1289191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 1290191783Srmacklem if (dp != NULL) { 1291191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 1292191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1293191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1294191783Srmacklem break; 1295191783Srmacklem } 1296191783Srmacklem if (lp != NULL) { 1297191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 1298191783Srmacklem if (lop->nfslo_first >= end) 1299191783Srmacklem break; 1300191783Srmacklem if (lop->nfslo_end <= off) 1301191783Srmacklem continue; 1302191783Srmacklem if (lop->nfslo_type == F_WRLCK) { 1303191783Srmacklem nfscl_clrelease(clp); 1304191783Srmacklem NFSUNLOCKCLSTATE(); 1305191783Srmacklem return (1); 1306191783Srmacklem } 1307191783Srmacklem } 1308191783Srmacklem } 1309191783Srmacklem } 1310191783Srmacklem 1311191783Srmacklem /* 1312191783Srmacklem * Now, check state against the server. 1313191783Srmacklem */ 1314191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1315191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1316191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1317191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1318191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1319191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1320191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1321191783Srmacklem break; 1322191783Srmacklem } 1323191783Srmacklem if (lp != NULL) { 1324191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 1325191783Srmacklem if (lop->nfslo_first >= end) 1326191783Srmacklem break; 1327191783Srmacklem if (lop->nfslo_end <= off) 1328191783Srmacklem continue; 1329191783Srmacklem if (lop->nfslo_type == F_WRLCK) { 1330191783Srmacklem nfscl_clrelease(clp); 1331191783Srmacklem NFSUNLOCKCLSTATE(); 1332191783Srmacklem return (1); 1333191783Srmacklem } 1334191783Srmacklem } 1335191783Srmacklem } 1336191783Srmacklem } 1337191783Srmacklem } 1338191783Srmacklem } 1339191783Srmacklem nfscl_clrelease(clp); 1340191783Srmacklem NFSUNLOCKCLSTATE(); 1341191783Srmacklem return (0); 1342191783Srmacklem} 1343191783Srmacklem 1344191783Srmacklem/* 1345191783Srmacklem * Release a byte range lock owner structure. 1346191783Srmacklem */ 1347191783SrmacklemAPPLESTATIC void 1348191783Srmacklemnfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete) 1349191783Srmacklem{ 1350191783Srmacklem struct nfsclclient *clp; 1351191783Srmacklem 1352191783Srmacklem if (lp == NULL) 1353191783Srmacklem return; 1354191783Srmacklem NFSLOCKCLSTATE(); 1355191783Srmacklem clp = lp->nfsl_open->nfso_own->nfsow_clp; 1356191783Srmacklem if (error != 0 && candelete && 1357191783Srmacklem (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0) 1358191783Srmacklem nfscl_freelockowner(lp, 0); 1359191783Srmacklem else 1360191783Srmacklem nfscl_lockunlock(&lp->nfsl_rwlock); 1361191783Srmacklem nfscl_clrelease(clp); 1362191783Srmacklem NFSUNLOCKCLSTATE(); 1363191783Srmacklem} 1364191783Srmacklem 1365191783Srmacklem/* 1366191783Srmacklem * Free up an open structure and any associated byte range lock structures. 1367191783Srmacklem */ 1368191783SrmacklemAPPLESTATIC void 1369191783Srmacklemnfscl_freeopen(struct nfsclopen *op, int local) 1370191783Srmacklem{ 1371191783Srmacklem 1372191783Srmacklem LIST_REMOVE(op, nfso_list); 1373191783Srmacklem nfscl_freealllocks(&op->nfso_lock, local); 1374191783Srmacklem FREE((caddr_t)op, M_NFSCLOPEN); 1375191783Srmacklem if (local) 1376191783Srmacklem newnfsstats.cllocalopens--; 1377191783Srmacklem else 1378191783Srmacklem newnfsstats.clopens--; 1379191783Srmacklem} 1380191783Srmacklem 1381191783Srmacklem/* 1382191783Srmacklem * Free up all lock owners and associated locks. 1383191783Srmacklem */ 1384191783Srmacklemstatic void 1385191783Srmacklemnfscl_freealllocks(struct nfscllockownerhead *lhp, int local) 1386191783Srmacklem{ 1387191783Srmacklem struct nfscllockowner *lp, *nlp; 1388191783Srmacklem 1389191783Srmacklem LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) { 1390191783Srmacklem if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED)) 1391191783Srmacklem panic("nfscllckw"); 1392191783Srmacklem nfscl_freelockowner(lp, local); 1393191783Srmacklem } 1394191783Srmacklem} 1395191783Srmacklem 1396191783Srmacklem/* 1397191783Srmacklem * Called for an Open when NFSERR_EXPIRED is received from the server. 1398191783Srmacklem * If there are no byte range locks nor a Share Deny lost, try to do a 1399191783Srmacklem * fresh Open. Otherwise, free the open. 1400191783Srmacklem */ 1401191783Srmacklemstatic int 1402191783Srmacklemnfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op, 1403191783Srmacklem struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p) 1404191783Srmacklem{ 1405191783Srmacklem struct nfscllockowner *lp; 1406191783Srmacklem struct nfscldeleg *dp; 1407191783Srmacklem int mustdelete = 0, error; 1408191783Srmacklem 1409191783Srmacklem /* 1410191783Srmacklem * Look for any byte range lock(s). 1411191783Srmacklem */ 1412191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1413191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 1414191783Srmacklem mustdelete = 1; 1415191783Srmacklem break; 1416191783Srmacklem } 1417191783Srmacklem } 1418191783Srmacklem 1419191783Srmacklem /* 1420191783Srmacklem * If no byte range lock(s) nor a Share deny, try to re-open. 1421191783Srmacklem */ 1422191783Srmacklem if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) { 1423191783Srmacklem newnfs_copycred(&op->nfso_cred, cred); 1424191783Srmacklem dp = NULL; 1425191783Srmacklem error = nfsrpc_reopen(nmp, op->nfso_fh, 1426191783Srmacklem op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p); 1427191783Srmacklem if (error) { 1428191783Srmacklem mustdelete = 1; 1429191783Srmacklem if (dp != NULL) { 1430191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 1431191783Srmacklem dp = NULL; 1432191783Srmacklem } 1433191783Srmacklem } 1434191783Srmacklem if (dp != NULL) 1435191783Srmacklem nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh, 1436191783Srmacklem op->nfso_fhlen, cred, p, &dp); 1437191783Srmacklem } 1438191783Srmacklem 1439191783Srmacklem /* 1440191783Srmacklem * If a byte range lock or Share deny or couldn't re-open, free it. 1441191783Srmacklem */ 1442191783Srmacklem if (mustdelete) 1443191783Srmacklem nfscl_freeopen(op, 0); 1444191783Srmacklem return (mustdelete); 1445191783Srmacklem} 1446191783Srmacklem 1447191783Srmacklem/* 1448191783Srmacklem * Free up an open owner structure. 1449191783Srmacklem */ 1450191783Srmacklemstatic void 1451191783Srmacklemnfscl_freeopenowner(struct nfsclowner *owp, int local) 1452191783Srmacklem{ 1453191783Srmacklem 1454191783Srmacklem LIST_REMOVE(owp, nfsow_list); 1455191783Srmacklem FREE((caddr_t)owp, M_NFSCLOWNER); 1456191783Srmacklem if (local) 1457191783Srmacklem newnfsstats.cllocalopenowners--; 1458191783Srmacklem else 1459191783Srmacklem newnfsstats.clopenowners--; 1460191783Srmacklem} 1461191783Srmacklem 1462191783Srmacklem/* 1463191783Srmacklem * Free up a byte range lock owner structure. 1464191783Srmacklem */ 1465223747SrmacklemAPPLESTATIC void 1466191783Srmacklemnfscl_freelockowner(struct nfscllockowner *lp, int local) 1467191783Srmacklem{ 1468191783Srmacklem struct nfscllock *lop, *nlop; 1469191783Srmacklem 1470191783Srmacklem LIST_REMOVE(lp, nfsl_list); 1471191783Srmacklem LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) { 1472191783Srmacklem nfscl_freelock(lop, local); 1473191783Srmacklem } 1474191783Srmacklem FREE((caddr_t)lp, M_NFSCLLOCKOWNER); 1475191783Srmacklem if (local) 1476191783Srmacklem newnfsstats.cllocallockowners--; 1477191783Srmacklem else 1478191783Srmacklem newnfsstats.cllockowners--; 1479191783Srmacklem} 1480191783Srmacklem 1481191783Srmacklem/* 1482191783Srmacklem * Free up a byte range lock structure. 1483191783Srmacklem */ 1484191783SrmacklemAPPLESTATIC void 1485191783Srmacklemnfscl_freelock(struct nfscllock *lop, int local) 1486191783Srmacklem{ 1487191783Srmacklem 1488191783Srmacklem LIST_REMOVE(lop, nfslo_list); 1489191783Srmacklem FREE((caddr_t)lop, M_NFSCLLOCK); 1490191783Srmacklem if (local) 1491191783Srmacklem newnfsstats.cllocallocks--; 1492191783Srmacklem else 1493191783Srmacklem newnfsstats.cllocks--; 1494191783Srmacklem} 1495191783Srmacklem 1496191783Srmacklem/* 1497191783Srmacklem * Clean out the state related to a delegation. 1498191783Srmacklem */ 1499191783Srmacklemstatic void 1500191783Srmacklemnfscl_cleandeleg(struct nfscldeleg *dp) 1501191783Srmacklem{ 1502191783Srmacklem struct nfsclowner *owp, *nowp; 1503191783Srmacklem struct nfsclopen *op; 1504191783Srmacklem 1505191783Srmacklem LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) { 1506191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1507191783Srmacklem if (op != NULL) { 1508191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 1509191783Srmacklem panic("nfscleandel"); 1510191783Srmacklem nfscl_freeopen(op, 1); 1511191783Srmacklem } 1512191783Srmacklem nfscl_freeopenowner(owp, 1); 1513191783Srmacklem } 1514191783Srmacklem nfscl_freealllocks(&dp->nfsdl_lock, 1); 1515191783Srmacklem} 1516191783Srmacklem 1517191783Srmacklem/* 1518191783Srmacklem * Free a delegation. 1519191783Srmacklem */ 1520191783Srmacklemstatic void 1521191783Srmacklemnfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp) 1522191783Srmacklem{ 1523191783Srmacklem 1524191783Srmacklem TAILQ_REMOVE(hdp, dp, nfsdl_list); 1525191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 1526191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 1527191783Srmacklem newnfsstats.cldelegates--; 1528191783Srmacklem nfscl_delegcnt--; 1529191783Srmacklem} 1530191783Srmacklem 1531191783Srmacklem/* 1532191783Srmacklem * Free up all state related to this client structure. 1533191783Srmacklem */ 1534191783Srmacklemstatic void 1535191783Srmacklemnfscl_cleanclient(struct nfsclclient *clp) 1536191783Srmacklem{ 1537191783Srmacklem struct nfsclowner *owp, *nowp; 1538191783Srmacklem struct nfsclopen *op, *nop; 1539191783Srmacklem 1540191783Srmacklem /* Now, all the OpenOwners, etc. */ 1541191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1542191783Srmacklem LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) { 1543191783Srmacklem nfscl_freeopen(op, 0); 1544191783Srmacklem } 1545191783Srmacklem nfscl_freeopenowner(owp, 0); 1546191783Srmacklem } 1547191783Srmacklem} 1548191783Srmacklem 1549191783Srmacklem/* 1550191783Srmacklem * Called when an NFSERR_EXPIRED is received from the server. 1551191783Srmacklem */ 1552191783Srmacklemstatic void 1553191783Srmacklemnfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp, 1554191783Srmacklem struct ucred *cred, NFSPROC_T *p) 1555191783Srmacklem{ 1556191783Srmacklem struct nfsclowner *owp, *nowp, *towp; 1557191783Srmacklem struct nfsclopen *op, *nop, *top; 1558191783Srmacklem struct nfscldeleg *dp, *ndp; 1559191783Srmacklem int ret, printed = 0; 1560191783Srmacklem 1561191783Srmacklem /* 1562191783Srmacklem * First, merge locally issued Opens into the list for the server. 1563191783Srmacklem */ 1564191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 1565191783Srmacklem while (dp != NULL) { 1566191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 1567191783Srmacklem owp = LIST_FIRST(&dp->nfsdl_owner); 1568191783Srmacklem while (owp != NULL) { 1569191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1570191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1571191783Srmacklem if (op != NULL) { 1572191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 1573191783Srmacklem panic("nfsclexp"); 1574191783Srmacklem LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) { 1575191783Srmacklem if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner, 1576191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1577191783Srmacklem break; 1578191783Srmacklem } 1579191783Srmacklem if (towp != NULL) { 1580191783Srmacklem /* Merge opens in */ 1581191783Srmacklem LIST_FOREACH(top, &towp->nfsow_open, nfso_list) { 1582191783Srmacklem if (top->nfso_fhlen == op->nfso_fhlen && 1583191783Srmacklem !NFSBCMP(top->nfso_fh, op->nfso_fh, 1584191783Srmacklem op->nfso_fhlen)) { 1585191783Srmacklem top->nfso_mode |= op->nfso_mode; 1586191783Srmacklem top->nfso_opencnt += op->nfso_opencnt; 1587191783Srmacklem break; 1588191783Srmacklem } 1589191783Srmacklem } 1590191783Srmacklem if (top == NULL) { 1591191783Srmacklem /* Just add the open to the owner list */ 1592191783Srmacklem LIST_REMOVE(op, nfso_list); 1593191783Srmacklem op->nfso_own = towp; 1594191783Srmacklem LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list); 1595191783Srmacklem newnfsstats.cllocalopens--; 1596191783Srmacklem newnfsstats.clopens++; 1597191783Srmacklem } 1598191783Srmacklem } else { 1599191783Srmacklem /* Just add the openowner to the client list */ 1600191783Srmacklem LIST_REMOVE(owp, nfsow_list); 1601191783Srmacklem owp->nfsow_clp = clp; 1602191783Srmacklem LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list); 1603191783Srmacklem newnfsstats.cllocalopenowners--; 1604191783Srmacklem newnfsstats.clopenowners++; 1605191783Srmacklem newnfsstats.cllocalopens--; 1606191783Srmacklem newnfsstats.clopens++; 1607191783Srmacklem } 1608191783Srmacklem } 1609191783Srmacklem owp = nowp; 1610191783Srmacklem } 1611191783Srmacklem if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) { 1612191783Srmacklem printed = 1; 1613191783Srmacklem printf("nfsv4 expired locks lost\n"); 1614191783Srmacklem } 1615191783Srmacklem nfscl_cleandeleg(dp); 1616191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 1617191783Srmacklem dp = ndp; 1618191783Srmacklem } 1619191783Srmacklem if (!TAILQ_EMPTY(&clp->nfsc_deleg)) 1620191783Srmacklem panic("nfsclexp"); 1621191783Srmacklem 1622191783Srmacklem /* 1623191783Srmacklem * Now, try and reopen against the server. 1624191783Srmacklem */ 1625191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1626191783Srmacklem owp->nfsow_seqid = 0; 1627191783Srmacklem LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) { 1628191783Srmacklem ret = nfscl_expireopen(clp, op, nmp, cred, p); 1629191783Srmacklem if (ret && !printed) { 1630191783Srmacklem printed = 1; 1631191783Srmacklem printf("nfsv4 expired locks lost\n"); 1632191783Srmacklem } 1633191783Srmacklem } 1634191783Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) 1635191783Srmacklem nfscl_freeopenowner(owp, 0); 1636191783Srmacklem } 1637191783Srmacklem} 1638191783Srmacklem 1639191783Srmacklem/* 1640229599Srmacklem * This function must be called after the process represented by "own" has 1641229599Srmacklem * exited. Must be called with CLSTATE lock held. 1642191783Srmacklem */ 1643191783Srmacklemstatic void 1644191783Srmacklemnfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own) 1645191783Srmacklem{ 1646191783Srmacklem struct nfsclowner *owp, *nowp; 1647191783Srmacklem struct nfscllockowner *lp, *nlp; 1648191783Srmacklem struct nfscldeleg *dp; 1649191783Srmacklem 1650191783Srmacklem /* First, get rid of local locks on delegations. */ 1651191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 1652191783Srmacklem LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) { 1653191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) { 1654191783Srmacklem if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED)) 1655191783Srmacklem panic("nfscllckw"); 1656191783Srmacklem nfscl_freelockowner(lp, 1); 1657191783Srmacklem } 1658191783Srmacklem } 1659191783Srmacklem } 1660191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 1661191783Srmacklem while (owp != NULL) { 1662191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1663191783Srmacklem if (!NFSBCMP(owp->nfsow_owner, own, 1664191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1665191783Srmacklem /* 1666191783Srmacklem * If there are children that haven't closed the 1667191783Srmacklem * file descriptors yet, the opens will still be 1668191783Srmacklem * here. For that case, let the renew thread clear 1669191783Srmacklem * out the OpenOwner later. 1670191783Srmacklem */ 1671191783Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) 1672191783Srmacklem nfscl_freeopenowner(owp, 0); 1673191783Srmacklem else 1674191783Srmacklem owp->nfsow_defunct = 1; 1675191783Srmacklem } 1676191783Srmacklem owp = nowp; 1677191783Srmacklem } 1678191783Srmacklem} 1679191783Srmacklem 1680191783Srmacklem/* 1681229599Srmacklem * Find open/lock owners for processes that have exited. 1682191783Srmacklem */ 1683191783Srmacklemstatic void 1684229752Srmacklemnfscl_cleanupkext(struct nfsclclient *clp, struct nfscllockownerfhhead *lhp) 1685191783Srmacklem{ 1686191783Srmacklem struct nfsclowner *owp, *nowp; 1687229752Srmacklem struct nfsclopen *op; 1688229752Srmacklem struct nfscllockowner *lp, *nlp; 1689191783Srmacklem 1690191783Srmacklem NFSPROCLISTLOCK(); 1691191783Srmacklem NFSLOCKCLSTATE(); 1692191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1693229752Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1694229752Srmacklem LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp) { 1695229752Srmacklem if (LIST_EMPTY(&lp->nfsl_lock)) 1696229752Srmacklem nfscl_emptylockowner(lp, lhp); 1697229752Srmacklem } 1698229752Srmacklem } 1699191783Srmacklem if (nfscl_procdoesntexist(owp->nfsow_owner)) 1700191783Srmacklem nfscl_cleanup_common(clp, owp->nfsow_owner); 1701191783Srmacklem } 1702191783Srmacklem NFSUNLOCKCLSTATE(); 1703191783Srmacklem NFSPROCLISTUNLOCK(); 1704191783Srmacklem} 1705191783Srmacklem 1706229752Srmacklem/* 1707229752Srmacklem * Take the empty lock owner and move it to the local lhp list if the 1708229752Srmacklem * associated process no longer exists. 1709229752Srmacklem */ 1710229752Srmacklemstatic void 1711229752Srmacklemnfscl_emptylockowner(struct nfscllockowner *lp, 1712229752Srmacklem struct nfscllockownerfhhead *lhp) 1713229752Srmacklem{ 1714229752Srmacklem struct nfscllockownerfh *lfhp, *mylfhp; 1715229752Srmacklem struct nfscllockowner *nlp; 1716229752Srmacklem int fnd_it; 1717229752Srmacklem 1718229752Srmacklem /* If not a Posix lock owner, just return. */ 1719229752Srmacklem if ((lp->nfsl_lockflags & F_POSIX) == 0) 1720229752Srmacklem return; 1721229752Srmacklem 1722229752Srmacklem fnd_it = 0; 1723229752Srmacklem mylfhp = NULL; 1724229752Srmacklem /* 1725229752Srmacklem * First, search to see if this lock owner is already in the list. 1726229752Srmacklem * If it is, then the associated process no longer exists. 1727229752Srmacklem */ 1728229752Srmacklem SLIST_FOREACH(lfhp, lhp, nfslfh_list) { 1729229752Srmacklem if (lfhp->nfslfh_len == lp->nfsl_open->nfso_fhlen && 1730229752Srmacklem !NFSBCMP(lfhp->nfslfh_fh, lp->nfsl_open->nfso_fh, 1731229752Srmacklem lfhp->nfslfh_len)) 1732229752Srmacklem mylfhp = lfhp; 1733229752Srmacklem LIST_FOREACH(nlp, &lfhp->nfslfh_lock, nfsl_list) 1734229752Srmacklem if (!NFSBCMP(nlp->nfsl_owner, lp->nfsl_owner, 1735229752Srmacklem NFSV4CL_LOCKNAMELEN)) 1736229752Srmacklem fnd_it = 1; 1737229752Srmacklem } 1738229752Srmacklem /* If not found, check if process still exists. */ 1739229752Srmacklem if (fnd_it == 0 && nfscl_procdoesntexist(lp->nfsl_owner) == 0) 1740229752Srmacklem return; 1741229752Srmacklem 1742229752Srmacklem /* Move the lock owner over to the local list. */ 1743229752Srmacklem if (mylfhp == NULL) { 1744229752Srmacklem mylfhp = malloc(sizeof(struct nfscllockownerfh), M_TEMP, 1745229752Srmacklem M_NOWAIT); 1746229752Srmacklem if (mylfhp == NULL) 1747229752Srmacklem return; 1748229752Srmacklem mylfhp->nfslfh_len = lp->nfsl_open->nfso_fhlen; 1749229752Srmacklem NFSBCOPY(lp->nfsl_open->nfso_fh, mylfhp->nfslfh_fh, 1750229752Srmacklem mylfhp->nfslfh_len); 1751229752Srmacklem LIST_INIT(&mylfhp->nfslfh_lock); 1752229752Srmacklem SLIST_INSERT_HEAD(lhp, mylfhp, nfslfh_list); 1753229752Srmacklem } 1754229752Srmacklem LIST_REMOVE(lp, nfsl_list); 1755229752Srmacklem LIST_INSERT_HEAD(&mylfhp->nfslfh_lock, lp, nfsl_list); 1756229752Srmacklem} 1757229752Srmacklem 1758222389Srmacklemstatic int fake_global; /* Used to force visibility of MNTK_UNMOUNTF */ 1759191783Srmacklem/* 1760191783Srmacklem * Called from nfs umount to free up the clientid. 1761191783Srmacklem */ 1762191783SrmacklemAPPLESTATIC void 1763191783Srmacklemnfscl_umount(struct nfsmount *nmp, NFSPROC_T *p) 1764191783Srmacklem{ 1765191783Srmacklem struct nfsclclient *clp; 1766191783Srmacklem struct ucred *cred; 1767191783Srmacklem int igotlock; 1768191783Srmacklem 1769222389Srmacklem /* 1770222389Srmacklem * For the case that matters, this is the thread that set 1771222389Srmacklem * MNTK_UNMOUNTF, so it will see it set. The code that follows is 1772222389Srmacklem * done to ensure that any thread executing nfscl_getcl() after 1773222389Srmacklem * this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the 1774222389Srmacklem * mutex for NFSLOCKCLSTATE(), so it is "m" for the following 1775222389Srmacklem * explanation, courtesy of Alan Cox. 1776222389Srmacklem * What follows is a snippet from Alan Cox's email at: 1777222389Srmacklem * http://docs.FreeBSD.org/cgi/ 1778222389Srmacklem * mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw 1779222389Srmacklem * 1780222389Srmacklem * 1. Set MNTK_UNMOUNTF 1781222389Srmacklem * 2. Acquire a standard FreeBSD mutex "m". 1782222389Srmacklem * 3. Update some data structures. 1783222389Srmacklem * 4. Release mutex "m". 1784222389Srmacklem * 1785222389Srmacklem * Then, other threads that acquire "m" after step 4 has occurred will 1786222389Srmacklem * see MNTK_UNMOUNTF as set. But, other threads that beat thread X to 1787222389Srmacklem * step 2 may or may not see MNTK_UNMOUNTF as set. 1788222389Srmacklem */ 1789222389Srmacklem NFSLOCKCLSTATE(); 1790222389Srmacklem if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 1791222389Srmacklem fake_global++; 1792222389Srmacklem NFSUNLOCKCLSTATE(); 1793222389Srmacklem NFSLOCKCLSTATE(); 1794222389Srmacklem } 1795222389Srmacklem 1796191783Srmacklem clp = nmp->nm_clp; 1797191783Srmacklem if (clp != NULL) { 1798191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0) 1799191783Srmacklem panic("nfscl umount"); 1800191783Srmacklem 1801191783Srmacklem /* 1802191783Srmacklem * First, handshake with the nfscl renew thread, to terminate 1803191783Srmacklem * it. 1804191783Srmacklem */ 1805191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_UMOUNT; 1806191783Srmacklem while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD) 1807222389Srmacklem (void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, 1808222389Srmacklem "nfsclumnt", hz); 1809191783Srmacklem 1810222389Srmacklem /* 1811222389Srmacklem * Now, get the exclusive lock on the client state, so 1812222389Srmacklem * that no uses of the state are still in progress. 1813222389Srmacklem */ 1814191783Srmacklem do { 1815191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 1816222389Srmacklem NFSCLSTATEMUTEXPTR, NULL); 1817191783Srmacklem } while (!igotlock); 1818191783Srmacklem NFSUNLOCKCLSTATE(); 1819191783Srmacklem 1820191783Srmacklem /* 1821191783Srmacklem * Free up all the state. It will expire on the server, but 1822191783Srmacklem * maybe we should do a SetClientId/SetClientIdConfirm so 1823191783Srmacklem * the server throws it away? 1824191783Srmacklem */ 1825191783Srmacklem LIST_REMOVE(clp, nfsc_list); 1826191783Srmacklem nfscl_delegreturnall(clp, p); 1827191783Srmacklem cred = newnfs_getcred(); 1828191783Srmacklem (void) nfsrpc_setclient(nmp, clp, cred, p); 1829191783Srmacklem nfscl_cleanclient(clp); 1830191783Srmacklem nmp->nm_clp = NULL; 1831191783Srmacklem NFSFREECRED(cred); 1832191783Srmacklem FREE((caddr_t)clp, M_NFSCLCLIENT); 1833222389Srmacklem } else 1834222389Srmacklem NFSUNLOCKCLSTATE(); 1835191783Srmacklem} 1836191783Srmacklem 1837191783Srmacklem/* 1838191783Srmacklem * This function is called when a server replies with NFSERR_STALECLIENTID 1839191783Srmacklem * or NFSERR_STALESTATEID. It traverses the clientid lists, doing Opens 1840191783Srmacklem * and Locks with reclaim. If these fail, it deletes the corresponding state. 1841191783Srmacklem */ 1842191783Srmacklemstatic void 1843191783Srmacklemnfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p) 1844191783Srmacklem{ 1845191783Srmacklem struct nfsclowner *owp, *nowp; 1846191783Srmacklem struct nfsclopen *op, *nop; 1847191783Srmacklem struct nfscllockowner *lp, *nlp; 1848191783Srmacklem struct nfscllock *lop, *nlop; 1849191783Srmacklem struct nfscldeleg *dp, *ndp, *tdp; 1850191783Srmacklem struct nfsmount *nmp; 1851191783Srmacklem struct ucred *tcred; 1852191783Srmacklem struct nfsclopenhead extra_open; 1853191783Srmacklem struct nfscldeleghead extra_deleg; 1854191783Srmacklem struct nfsreq *rep; 1855191783Srmacklem u_int64_t len; 1856191783Srmacklem u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode; 1857191783Srmacklem int igotlock = 0, error, trycnt, firstlock, s; 1858191783Srmacklem 1859191783Srmacklem /* 1860191783Srmacklem * First, lock the client structure, so everyone else will 1861191783Srmacklem * block when trying to use state. 1862191783Srmacklem */ 1863191783Srmacklem NFSLOCKCLSTATE(); 1864206818Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG; 1865191783Srmacklem do { 1866191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 1867222389Srmacklem NFSCLSTATEMUTEXPTR, NULL); 1868191783Srmacklem } while (!igotlock); 1869191783Srmacklem NFSUNLOCKCLSTATE(); 1870191783Srmacklem 1871191783Srmacklem nmp = clp->nfsc_nmp; 1872191783Srmacklem if (nmp == NULL) 1873191783Srmacklem panic("nfscl recover"); 1874191783Srmacklem trycnt = 5; 1875191783Srmacklem do { 1876191783Srmacklem error = nfsrpc_setclient(nmp, clp, cred, p); 1877191783Srmacklem } while ((error == NFSERR_STALECLIENTID || 1878191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); 1879191783Srmacklem if (error) { 1880191783Srmacklem nfscl_cleanclient(clp); 1881206818Srmacklem NFSLOCKCLSTATE(); 1882191783Srmacklem clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID | 1883206818Srmacklem NFSCLFLAGS_RECOVER | NFSCLFLAGS_RECVRINPROG); 1884206818Srmacklem wakeup(&clp->nfsc_flags); 1885191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 1886191783Srmacklem NFSUNLOCKCLSTATE(); 1887191783Srmacklem return; 1888191783Srmacklem } 1889191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 1890191783Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER; 1891191783Srmacklem 1892191783Srmacklem /* 1893191783Srmacklem * Mark requests already queued on the server, so that they don't 1894191783Srmacklem * initiate another recovery cycle. Any requests already in the 1895191783Srmacklem * queue that handle state information will have the old stale 1896191783Srmacklem * clientid/stateid and will get a NFSERR_STALESTATEID or 1897191783Srmacklem * NFSERR_STALECLIENTID reply from the server. This will be 1898191783Srmacklem * translated to NFSERR_STALEDONTRECOVER when R_DONTRECOVER is set. 1899191783Srmacklem */ 1900191783Srmacklem s = splsoftclock(); 1901191783Srmacklem NFSLOCKREQ(); 1902191783Srmacklem TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) { 1903191783Srmacklem if (rep->r_nmp == nmp) 1904191783Srmacklem rep->r_flags |= R_DONTRECOVER; 1905191783Srmacklem } 1906191783Srmacklem NFSUNLOCKREQ(); 1907191783Srmacklem splx(s); 1908191783Srmacklem 1909191783Srmacklem /* 1910191783Srmacklem * Now, mark all delegations "need reclaim". 1911191783Srmacklem */ 1912191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) 1913191783Srmacklem dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM; 1914191783Srmacklem 1915191783Srmacklem TAILQ_INIT(&extra_deleg); 1916191783Srmacklem LIST_INIT(&extra_open); 1917191783Srmacklem /* 1918191783Srmacklem * Now traverse the state lists, doing Open and Lock Reclaims. 1919191783Srmacklem */ 1920191783Srmacklem tcred = newnfs_getcred(); 1921191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 1922191783Srmacklem while (owp != NULL) { 1923191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1924191783Srmacklem owp->nfsow_seqid = 0; 1925191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1926191783Srmacklem while (op != NULL) { 1927191783Srmacklem nop = LIST_NEXT(op, nfso_list); 1928191783Srmacklem if (error != NFSERR_NOGRACE) { 1929191783Srmacklem /* Search for a delegation to reclaim with the open */ 1930191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 1931191783Srmacklem if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) 1932191783Srmacklem continue; 1933191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE)) { 1934191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 1935191783Srmacklem delegtype = NFSV4OPEN_DELEGATEWRITE; 1936191783Srmacklem } else { 1937191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 1938191783Srmacklem delegtype = NFSV4OPEN_DELEGATEREAD; 1939191783Srmacklem } 1940191783Srmacklem if ((op->nfso_mode & mode) == mode && 1941191783Srmacklem op->nfso_fhlen == dp->nfsdl_fhlen && 1942191783Srmacklem !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen)) 1943191783Srmacklem break; 1944191783Srmacklem } 1945191783Srmacklem ndp = dp; 1946191783Srmacklem if (dp == NULL) 1947191783Srmacklem delegtype = NFSV4OPEN_DELEGATENONE; 1948191783Srmacklem newnfs_copycred(&op->nfso_cred, tcred); 1949191783Srmacklem error = nfscl_tryopen(nmp, NULL, op->nfso_fh, 1950191783Srmacklem op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen, 1951191783Srmacklem op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype, 1952191783Srmacklem tcred, p); 1953191783Srmacklem if (!error) { 1954191783Srmacklem /* Handle any replied delegation */ 1955191783Srmacklem if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE) 1956191783Srmacklem || NFSMNT_RDONLY(nmp->nm_mountp))) { 1957191783Srmacklem if ((ndp->nfsdl_flags & NFSCLDL_WRITE)) 1958191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 1959191783Srmacklem else 1960191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 1961191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 1962191783Srmacklem if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) 1963191783Srmacklem continue; 1964191783Srmacklem if ((op->nfso_mode & mode) == mode && 1965191783Srmacklem op->nfso_fhlen == dp->nfsdl_fhlen && 1966191783Srmacklem !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, 1967191783Srmacklem op->nfso_fhlen)) { 1968191783Srmacklem dp->nfsdl_stateid = ndp->nfsdl_stateid; 1969191783Srmacklem dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit; 1970191783Srmacklem dp->nfsdl_ace = ndp->nfsdl_ace; 1971191783Srmacklem dp->nfsdl_change = ndp->nfsdl_change; 1972191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM; 1973191783Srmacklem if ((ndp->nfsdl_flags & NFSCLDL_RECALL)) 1974191783Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 1975191783Srmacklem FREE((caddr_t)ndp, M_NFSCLDELEG); 1976191783Srmacklem ndp = NULL; 1977191783Srmacklem break; 1978191783Srmacklem } 1979191783Srmacklem } 1980191783Srmacklem } 1981191783Srmacklem if (ndp != NULL) 1982191783Srmacklem TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list); 1983191783Srmacklem 1984191783Srmacklem /* and reclaim all byte range locks */ 1985191783Srmacklem lp = LIST_FIRST(&op->nfso_lock); 1986191783Srmacklem while (lp != NULL) { 1987191783Srmacklem nlp = LIST_NEXT(lp, nfsl_list); 1988191783Srmacklem lp->nfsl_seqid = 0; 1989191783Srmacklem firstlock = 1; 1990191783Srmacklem lop = LIST_FIRST(&lp->nfsl_lock); 1991191783Srmacklem while (lop != NULL) { 1992191783Srmacklem nlop = LIST_NEXT(lop, nfslo_list); 1993191783Srmacklem if (lop->nfslo_end == NFS64BITSSET) 1994191783Srmacklem len = NFS64BITSSET; 1995191783Srmacklem else 1996191783Srmacklem len = lop->nfslo_end - lop->nfslo_first; 1997191783Srmacklem if (error != NFSERR_NOGRACE) 1998191783Srmacklem error = nfscl_trylock(nmp, NULL, 1999191783Srmacklem op->nfso_fh, op->nfso_fhlen, lp, 2000191783Srmacklem firstlock, 1, lop->nfslo_first, len, 2001191783Srmacklem lop->nfslo_type, tcred, p); 2002191783Srmacklem if (error != 0) 2003191783Srmacklem nfscl_freelock(lop, 0); 2004191783Srmacklem else 2005191783Srmacklem firstlock = 0; 2006191783Srmacklem lop = nlop; 2007191783Srmacklem } 2008191783Srmacklem /* If no locks, but a lockowner, just delete it. */ 2009191783Srmacklem if (LIST_EMPTY(&lp->nfsl_lock)) 2010191783Srmacklem nfscl_freelockowner(lp, 0); 2011191783Srmacklem lp = nlp; 2012191783Srmacklem } 2013191783Srmacklem } else { 2014191783Srmacklem nfscl_freeopen(op, 0); 2015191783Srmacklem } 2016191783Srmacklem } 2017191783Srmacklem op = nop; 2018191783Srmacklem } 2019191783Srmacklem owp = nowp; 2020191783Srmacklem } 2021191783Srmacklem 2022191783Srmacklem /* 2023191783Srmacklem * Now, try and get any delegations not yet reclaimed by cobbling 2024191783Srmacklem * to-gether an appropriate open. 2025191783Srmacklem */ 2026191783Srmacklem nowp = NULL; 2027191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 2028191783Srmacklem while (dp != NULL) { 2029191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 2030191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) { 2031191783Srmacklem if (nowp == NULL) { 2032191783Srmacklem MALLOC(nowp, struct nfsclowner *, 2033191783Srmacklem sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK); 2034191783Srmacklem /* 2035191783Srmacklem * Name must be as long an largest possible 2036191783Srmacklem * NFSV4CL_LOCKNAMELEN. 12 for now. 2037191783Srmacklem */ 2038191783Srmacklem NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner, 2039191783Srmacklem NFSV4CL_LOCKNAMELEN); 2040191783Srmacklem LIST_INIT(&nowp->nfsow_open); 2041191783Srmacklem nowp->nfsow_clp = clp; 2042191783Srmacklem nowp->nfsow_seqid = 0; 2043191783Srmacklem nowp->nfsow_defunct = 0; 2044191783Srmacklem nfscl_lockinit(&nowp->nfsow_rwlock); 2045191783Srmacklem } 2046191783Srmacklem nop = NULL; 2047191783Srmacklem if (error != NFSERR_NOGRACE) { 2048191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 2049191783Srmacklem dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK); 2050191783Srmacklem nop->nfso_own = nowp; 2051191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE)) { 2052191783Srmacklem nop->nfso_mode = NFSV4OPEN_ACCESSWRITE; 2053191783Srmacklem delegtype = NFSV4OPEN_DELEGATEWRITE; 2054191783Srmacklem } else { 2055191783Srmacklem nop->nfso_mode = NFSV4OPEN_ACCESSREAD; 2056191783Srmacklem delegtype = NFSV4OPEN_DELEGATEREAD; 2057191783Srmacklem } 2058191783Srmacklem nop->nfso_opencnt = 0; 2059191783Srmacklem nop->nfso_posixlock = 1; 2060191783Srmacklem nop->nfso_fhlen = dp->nfsdl_fhlen; 2061191783Srmacklem NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen); 2062191783Srmacklem LIST_INIT(&nop->nfso_lock); 2063191783Srmacklem nop->nfso_stateid.seqid = 0; 2064191783Srmacklem nop->nfso_stateid.other[0] = 0; 2065191783Srmacklem nop->nfso_stateid.other[1] = 0; 2066191783Srmacklem nop->nfso_stateid.other[2] = 0; 2067191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, tcred); 2068191783Srmacklem newnfs_copyincred(tcred, &nop->nfso_cred); 2069191783Srmacklem tdp = NULL; 2070191783Srmacklem error = nfscl_tryopen(nmp, NULL, nop->nfso_fh, 2071191783Srmacklem nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen, 2072191783Srmacklem nop->nfso_mode, nop, NULL, 0, &tdp, 1, 2073191783Srmacklem delegtype, tcred, p); 2074191783Srmacklem if (tdp != NULL) { 2075191783Srmacklem if ((tdp->nfsdl_flags & NFSCLDL_WRITE)) 2076191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 2077191783Srmacklem else 2078191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 2079191783Srmacklem if ((nop->nfso_mode & mode) == mode && 2080191783Srmacklem nop->nfso_fhlen == tdp->nfsdl_fhlen && 2081191783Srmacklem !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh, 2082191783Srmacklem nop->nfso_fhlen)) { 2083191783Srmacklem dp->nfsdl_stateid = tdp->nfsdl_stateid; 2084191783Srmacklem dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit; 2085191783Srmacklem dp->nfsdl_ace = tdp->nfsdl_ace; 2086191783Srmacklem dp->nfsdl_change = tdp->nfsdl_change; 2087191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM; 2088191783Srmacklem if ((tdp->nfsdl_flags & NFSCLDL_RECALL)) 2089191783Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 2090191783Srmacklem FREE((caddr_t)tdp, M_NFSCLDELEG); 2091191783Srmacklem } else { 2092191783Srmacklem TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list); 2093191783Srmacklem } 2094191783Srmacklem } 2095191783Srmacklem } 2096191783Srmacklem if (error) { 2097191783Srmacklem if (nop != NULL) 2098191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 2099191783Srmacklem /* 2100191783Srmacklem * Couldn't reclaim it, so throw the state 2101191783Srmacklem * away. Ouch!! 2102191783Srmacklem */ 2103191783Srmacklem nfscl_cleandeleg(dp); 2104191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 2105191783Srmacklem } else { 2106191783Srmacklem LIST_INSERT_HEAD(&extra_open, nop, nfso_list); 2107191783Srmacklem } 2108191783Srmacklem } 2109191783Srmacklem dp = ndp; 2110191783Srmacklem } 2111191783Srmacklem 2112191783Srmacklem /* 2113191783Srmacklem * Now, get rid of extra Opens and Delegations. 2114191783Srmacklem */ 2115191783Srmacklem LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) { 2116191783Srmacklem do { 2117191783Srmacklem newnfs_copycred(&op->nfso_cred, tcred); 2118191783Srmacklem error = nfscl_tryclose(op, tcred, nmp, p); 2119191783Srmacklem if (error == NFSERR_GRACE) 2120207170Srmacklem (void) nfs_catnap(PZERO, error, "nfsexcls"); 2121191783Srmacklem } while (error == NFSERR_GRACE); 2122191783Srmacklem LIST_REMOVE(op, nfso_list); 2123191783Srmacklem FREE((caddr_t)op, M_NFSCLOPEN); 2124191783Srmacklem } 2125191783Srmacklem if (nowp != NULL) 2126191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 2127191783Srmacklem 2128191783Srmacklem TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) { 2129191783Srmacklem do { 2130191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, tcred); 2131191783Srmacklem error = nfscl_trydelegreturn(dp, tcred, nmp, p); 2132191783Srmacklem if (error == NFSERR_GRACE) 2133207170Srmacklem (void) nfs_catnap(PZERO, error, "nfsexdlg"); 2134191783Srmacklem } while (error == NFSERR_GRACE); 2135191783Srmacklem TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list); 2136191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 2137191783Srmacklem } 2138191783Srmacklem 2139191783Srmacklem NFSLOCKCLSTATE(); 2140206818Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG; 2141206818Srmacklem wakeup(&clp->nfsc_flags); 2142191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2143191783Srmacklem NFSUNLOCKCLSTATE(); 2144191783Srmacklem NFSFREECRED(tcred); 2145191783Srmacklem} 2146191783Srmacklem 2147191783Srmacklem/* 2148191783Srmacklem * This function is called when a server replies with NFSERR_EXPIRED. 2149191783Srmacklem * It deletes all state for the client and does a fresh SetClientId/confirm. 2150191783Srmacklem * XXX Someday it should post a signal to the process(es) that hold the 2151191783Srmacklem * state, so they know that lock state has been lost. 2152191783Srmacklem */ 2153191783SrmacklemAPPLESTATIC int 2154191783Srmacklemnfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p) 2155191783Srmacklem{ 2156191783Srmacklem struct nfsmount *nmp; 2157191783Srmacklem struct ucred *cred; 2158191783Srmacklem int igotlock = 0, error, trycnt; 2159191783Srmacklem 2160191783Srmacklem /* 2161191783Srmacklem * If the clientid has gone away or a new SetClientid has already 2162191783Srmacklem * been done, just return ok. 2163191783Srmacklem */ 2164191783Srmacklem if (clp == NULL || clidrev != clp->nfsc_clientidrev) 2165191783Srmacklem return (0); 2166191783Srmacklem 2167191783Srmacklem /* 2168191783Srmacklem * First, lock the client structure, so everyone else will 2169191783Srmacklem * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so 2170191783Srmacklem * that only one thread does the work. 2171191783Srmacklem */ 2172191783Srmacklem NFSLOCKCLSTATE(); 2173191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT; 2174191783Srmacklem do { 2175191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 2176222389Srmacklem NFSCLSTATEMUTEXPTR, NULL); 2177191783Srmacklem } while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT)); 2178191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) { 2179191783Srmacklem if (igotlock) 2180191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2181191783Srmacklem NFSUNLOCKCLSTATE(); 2182191783Srmacklem return (0); 2183191783Srmacklem } 2184206880Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG; 2185191783Srmacklem NFSUNLOCKCLSTATE(); 2186191783Srmacklem 2187191783Srmacklem nmp = clp->nfsc_nmp; 2188191783Srmacklem if (nmp == NULL) 2189191783Srmacklem panic("nfscl expired"); 2190191783Srmacklem cred = newnfs_getcred(); 2191191783Srmacklem trycnt = 5; 2192191783Srmacklem do { 2193191783Srmacklem error = nfsrpc_setclient(nmp, clp, cred, p); 2194191783Srmacklem } while ((error == NFSERR_STALECLIENTID || 2195191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); 2196191783Srmacklem if (error) { 2197191783Srmacklem /* 2198191783Srmacklem * Clear out any state. 2199191783Srmacklem */ 2200191783Srmacklem nfscl_cleanclient(clp); 2201206880Srmacklem NFSLOCKCLSTATE(); 2202191783Srmacklem clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID | 2203191783Srmacklem NFSCLFLAGS_RECOVER); 2204191783Srmacklem } else { 2205191783Srmacklem /* 2206191783Srmacklem * Expire the state for the client. 2207191783Srmacklem */ 2208191783Srmacklem nfscl_expireclient(clp, nmp, cred, p); 2209206880Srmacklem NFSLOCKCLSTATE(); 2210191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 2211191783Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER; 2212191783Srmacklem } 2213206880Srmacklem clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG); 2214206880Srmacklem wakeup(&clp->nfsc_flags); 2215191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2216191783Srmacklem NFSUNLOCKCLSTATE(); 2217206880Srmacklem NFSFREECRED(cred); 2218191783Srmacklem return (error); 2219191783Srmacklem} 2220191783Srmacklem 2221191783Srmacklem/* 2222191783Srmacklem * This function inserts a lock in the list after insert_lop. 2223191783Srmacklem */ 2224191783Srmacklemstatic void 2225191783Srmacklemnfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop, 2226191783Srmacklem struct nfscllock *insert_lop, int local) 2227191783Srmacklem{ 2228191783Srmacklem 2229191783Srmacklem if ((struct nfscllockowner *)insert_lop == lp) 2230191783Srmacklem LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list); 2231191783Srmacklem else 2232191783Srmacklem LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list); 2233191783Srmacklem if (local) 2234191783Srmacklem newnfsstats.cllocallocks++; 2235191783Srmacklem else 2236191783Srmacklem newnfsstats.cllocks++; 2237191783Srmacklem} 2238191783Srmacklem 2239191783Srmacklem/* 2240191783Srmacklem * This function updates the locking for a lock owner and given file. It 2241191783Srmacklem * maintains a list of lock ranges ordered on increasing file offset that 2242191783Srmacklem * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style). 2243191783Srmacklem * It always adds new_lop to the list and sometimes uses the one pointed 2244191783Srmacklem * at by other_lopp. 2245191783Srmacklem * Returns 1 if the locks were modified, 0 otherwise. 2246191783Srmacklem */ 2247191783Srmacklemstatic int 2248191783Srmacklemnfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp, 2249191783Srmacklem struct nfscllock **other_lopp, int local) 2250191783Srmacklem{ 2251191783Srmacklem struct nfscllock *new_lop = *new_lopp; 2252191783Srmacklem struct nfscllock *lop, *tlop, *ilop; 2253191783Srmacklem struct nfscllock *other_lop; 2254191783Srmacklem int unlock = 0, modified = 0; 2255191783Srmacklem u_int64_t tmp; 2256191783Srmacklem 2257191783Srmacklem /* 2258191783Srmacklem * Work down the list until the lock is merged. 2259191783Srmacklem */ 2260191783Srmacklem if (new_lop->nfslo_type == F_UNLCK) 2261191783Srmacklem unlock = 1; 2262191783Srmacklem ilop = (struct nfscllock *)lp; 2263191783Srmacklem lop = LIST_FIRST(&lp->nfsl_lock); 2264191783Srmacklem while (lop != NULL) { 2265191783Srmacklem /* 2266191783Srmacklem * Only check locks for this file that aren't before the start of 2267191783Srmacklem * new lock's range. 2268191783Srmacklem */ 2269191783Srmacklem if (lop->nfslo_end >= new_lop->nfslo_first) { 2270191783Srmacklem if (new_lop->nfslo_end < lop->nfslo_first) { 2271191783Srmacklem /* 2272191783Srmacklem * If the new lock ends before the start of the 2273191783Srmacklem * current lock's range, no merge, just insert 2274191783Srmacklem * the new lock. 2275191783Srmacklem */ 2276191783Srmacklem break; 2277191783Srmacklem } 2278191783Srmacklem if (new_lop->nfslo_type == lop->nfslo_type || 2279191783Srmacklem (new_lop->nfslo_first <= lop->nfslo_first && 2280191783Srmacklem new_lop->nfslo_end >= lop->nfslo_end)) { 2281191783Srmacklem /* 2282191783Srmacklem * This lock can be absorbed by the new lock/unlock. 2283191783Srmacklem * This happens when it covers the entire range 2284191783Srmacklem * of the old lock or is contiguous 2285191783Srmacklem * with the old lock and is of the same type or an 2286191783Srmacklem * unlock. 2287191783Srmacklem */ 2288191783Srmacklem if (new_lop->nfslo_type != lop->nfslo_type || 2289191783Srmacklem new_lop->nfslo_first != lop->nfslo_first || 2290191783Srmacklem new_lop->nfslo_end != lop->nfslo_end) 2291191783Srmacklem modified = 1; 2292191783Srmacklem if (lop->nfslo_first < new_lop->nfslo_first) 2293191783Srmacklem new_lop->nfslo_first = lop->nfslo_first; 2294191783Srmacklem if (lop->nfslo_end > new_lop->nfslo_end) 2295191783Srmacklem new_lop->nfslo_end = lop->nfslo_end; 2296191783Srmacklem tlop = lop; 2297191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2298191783Srmacklem nfscl_freelock(tlop, local); 2299191783Srmacklem continue; 2300191783Srmacklem } 2301191783Srmacklem 2302191783Srmacklem /* 2303191783Srmacklem * All these cases are for contiguous locks that are not the 2304191783Srmacklem * same type, so they can't be merged. 2305191783Srmacklem */ 2306191783Srmacklem if (new_lop->nfslo_first <= lop->nfslo_first) { 2307191783Srmacklem /* 2308191783Srmacklem * This case is where the new lock overlaps with the 2309191783Srmacklem * first part of the old lock. Move the start of the 2310191783Srmacklem * old lock to just past the end of the new lock. The 2311191783Srmacklem * new lock will be inserted in front of the old, since 2312191783Srmacklem * ilop hasn't been updated. (We are done now.) 2313191783Srmacklem */ 2314191783Srmacklem if (lop->nfslo_first != new_lop->nfslo_end) { 2315191783Srmacklem lop->nfslo_first = new_lop->nfslo_end; 2316191783Srmacklem modified = 1; 2317191783Srmacklem } 2318191783Srmacklem break; 2319191783Srmacklem } 2320191783Srmacklem if (new_lop->nfslo_end >= lop->nfslo_end) { 2321191783Srmacklem /* 2322191783Srmacklem * This case is where the new lock overlaps with the 2323191783Srmacklem * end of the old lock's range. Move the old lock's 2324191783Srmacklem * end to just before the new lock's first and insert 2325191783Srmacklem * the new lock after the old lock. 2326191783Srmacklem * Might not be done yet, since the new lock could 2327191783Srmacklem * overlap further locks with higher ranges. 2328191783Srmacklem */ 2329191783Srmacklem if (lop->nfslo_end != new_lop->nfslo_first) { 2330191783Srmacklem lop->nfslo_end = new_lop->nfslo_first; 2331191783Srmacklem modified = 1; 2332191783Srmacklem } 2333191783Srmacklem ilop = lop; 2334191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2335191783Srmacklem continue; 2336191783Srmacklem } 2337191783Srmacklem /* 2338191783Srmacklem * The final case is where the new lock's range is in the 2339191783Srmacklem * middle of the current lock's and splits the current lock 2340191783Srmacklem * up. Use *other_lopp to handle the second part of the 2341191783Srmacklem * split old lock range. (We are done now.) 2342191783Srmacklem * For unlock, we use new_lop as other_lop and tmp, since 2343191783Srmacklem * other_lop and new_lop are the same for this case. 2344191783Srmacklem * We noted the unlock case above, so we don't need 2345191783Srmacklem * new_lop->nfslo_type any longer. 2346191783Srmacklem */ 2347191783Srmacklem tmp = new_lop->nfslo_first; 2348191783Srmacklem if (unlock) { 2349191783Srmacklem other_lop = new_lop; 2350191783Srmacklem *new_lopp = NULL; 2351191783Srmacklem } else { 2352191783Srmacklem other_lop = *other_lopp; 2353191783Srmacklem *other_lopp = NULL; 2354191783Srmacklem } 2355191783Srmacklem other_lop->nfslo_first = new_lop->nfslo_end; 2356191783Srmacklem other_lop->nfslo_end = lop->nfslo_end; 2357191783Srmacklem other_lop->nfslo_type = lop->nfslo_type; 2358191783Srmacklem lop->nfslo_end = tmp; 2359191783Srmacklem nfscl_insertlock(lp, other_lop, lop, local); 2360191783Srmacklem ilop = lop; 2361191783Srmacklem modified = 1; 2362191783Srmacklem break; 2363191783Srmacklem } 2364191783Srmacklem ilop = lop; 2365191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2366191783Srmacklem if (lop == NULL) 2367191783Srmacklem break; 2368191783Srmacklem } 2369191783Srmacklem 2370191783Srmacklem /* 2371191783Srmacklem * Insert the new lock in the list at the appropriate place. 2372191783Srmacklem */ 2373191783Srmacklem if (!unlock) { 2374191783Srmacklem nfscl_insertlock(lp, new_lop, ilop, local); 2375191783Srmacklem *new_lopp = NULL; 2376191783Srmacklem modified = 1; 2377191783Srmacklem } 2378191783Srmacklem return (modified); 2379191783Srmacklem} 2380191783Srmacklem 2381191783Srmacklem/* 2382191783Srmacklem * This function must be run as a kernel thread. 2383191783Srmacklem * It does Renew Ops and recovery, when required. 2384191783Srmacklem */ 2385191783SrmacklemAPPLESTATIC void 2386191783Srmacklemnfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p) 2387191783Srmacklem{ 2388191783Srmacklem struct nfsclowner *owp, *nowp; 2389191783Srmacklem struct nfsclopen *op; 2390229752Srmacklem struct nfscllockowner *lp, *nlp; 2391191783Srmacklem struct nfscldeleghead dh; 2392191783Srmacklem struct nfscldeleg *dp, *ndp; 2393191783Srmacklem struct ucred *cred; 2394191783Srmacklem u_int32_t clidrev; 2395191783Srmacklem int error, cbpathdown, islept, igotlock, ret, clearok; 2396206818Srmacklem uint32_t recover_done_time = 0; 2397247502Sjhb time_t mytime; 2398229678Srmacklem static time_t prevsec = 0; 2399229752Srmacklem struct nfscllockownerfh *lfhp, *nlfhp; 2400229752Srmacklem struct nfscllockownerfhhead lfh; 2401191783Srmacklem 2402191783Srmacklem cred = newnfs_getcred(); 2403206690Srmacklem NFSLOCKCLSTATE(); 2404191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD; 2405206690Srmacklem NFSUNLOCKCLSTATE(); 2406191783Srmacklem for(;;) { 2407191783Srmacklem newnfs_setroot(cred); 2408191783Srmacklem cbpathdown = 0; 2409206818Srmacklem if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) { 2410206818Srmacklem /* 2411206818Srmacklem * Only allow one recover within 1/2 of the lease 2412206818Srmacklem * duration (nfsc_renew). 2413206818Srmacklem */ 2414206818Srmacklem if (recover_done_time < NFSD_MONOSEC) { 2415206818Srmacklem recover_done_time = NFSD_MONOSEC + 2416206818Srmacklem clp->nfsc_renew; 2417206818Srmacklem nfscl_recover(clp, cred, p); 2418206818Srmacklem } else { 2419206818Srmacklem NFSLOCKCLSTATE(); 2420206818Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER; 2421206818Srmacklem NFSUNLOCKCLSTATE(); 2422206818Srmacklem } 2423206818Srmacklem } 2424191783Srmacklem if (clp->nfsc_expire <= NFSD_MONOSEC && 2425191783Srmacklem (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) { 2426191783Srmacklem clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew; 2427191783Srmacklem clidrev = clp->nfsc_clientidrev; 2428191783Srmacklem error = nfsrpc_renew(clp, cred, p); 2429191783Srmacklem if (error == NFSERR_CBPATHDOWN) 2430191783Srmacklem cbpathdown = 1; 2431206690Srmacklem else if (error == NFSERR_STALECLIENTID) { 2432206690Srmacklem NFSLOCKCLSTATE(); 2433191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECOVER; 2434206690Srmacklem NFSUNLOCKCLSTATE(); 2435206690Srmacklem } else if (error == NFSERR_EXPIRED) 2436191783Srmacklem (void) nfscl_hasexpired(clp, clidrev, p); 2437191783Srmacklem } 2438191783Srmacklem 2439191783Srmacklem TAILQ_INIT(&dh); 2440191783Srmacklem NFSLOCKCLSTATE(); 2441191783Srmacklem if (cbpathdown) 2442191783Srmacklem /* It's a Total Recall! */ 2443191783Srmacklem nfscl_totalrecall(clp); 2444191783Srmacklem 2445191783Srmacklem /* 2446191783Srmacklem * Now, handle defunct owners. 2447191783Srmacklem */ 2448229752Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 2449229752Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) { 2450229752Srmacklem if (owp->nfsow_defunct != 0) 2451229752Srmacklem nfscl_freeopenowner(owp, 0); 2452191783Srmacklem } 2453191783Srmacklem } 2454191783Srmacklem 2455191783Srmacklem /* 2456191783Srmacklem * Do the recall on any delegations. To avoid trouble, always 2457191783Srmacklem * come back up here after having slept. 2458191783Srmacklem */ 2459191783Srmacklem igotlock = 0; 2460191783Srmacklemtryagain: 2461191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 2462191783Srmacklem while (dp != NULL) { 2463191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 2464191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_RECALL)) { 2465191783Srmacklem /* 2466191783Srmacklem * Wait for outstanding I/O ops to be done. 2467191783Srmacklem */ 2468191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 2469191783Srmacklem if (igotlock) { 2470191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2471191783Srmacklem igotlock = 0; 2472191783Srmacklem } 2473191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= 2474191783Srmacklem NFSV4LOCK_WANTED; 2475191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 2476191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", 2477191783Srmacklem NULL); 2478191783Srmacklem goto tryagain; 2479191783Srmacklem } 2480191783Srmacklem while (!igotlock) { 2481191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 2482222389Srmacklem &islept, NFSCLSTATEMUTEXPTR, NULL); 2483191783Srmacklem if (islept) 2484191783Srmacklem goto tryagain; 2485191783Srmacklem } 2486191783Srmacklem NFSUNLOCKCLSTATE(); 2487191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 2488191783Srmacklem ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp, 2489207082Srmacklem NULL, cred, p, 1); 2490191783Srmacklem if (!ret) { 2491191783Srmacklem nfscl_cleandeleg(dp); 2492191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, 2493191783Srmacklem nfsdl_list); 2494191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 2495191783Srmacklem TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list); 2496191783Srmacklem nfscl_delegcnt--; 2497191783Srmacklem newnfsstats.cldelegates--; 2498191783Srmacklem } 2499191783Srmacklem NFSLOCKCLSTATE(); 2500191783Srmacklem } 2501191783Srmacklem dp = ndp; 2502191783Srmacklem } 2503191783Srmacklem 2504191783Srmacklem /* 2505191783Srmacklem * Clear out old delegations, if we are above the high water 2506191783Srmacklem * mark. Only clear out ones with no state related to them. 2507191783Srmacklem * The tailq list is in LRU order. 2508191783Srmacklem */ 2509191783Srmacklem dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead); 2510191783Srmacklem while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) { 2511191783Srmacklem ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list); 2512191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt == 0 && 2513191783Srmacklem dp->nfsdl_rwlock.nfslock_lock == 0 && 2514191783Srmacklem dp->nfsdl_timestamp < NFSD_MONOSEC && 2515214406Srmacklem (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED | 2516214406Srmacklem NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) { 2517191783Srmacklem clearok = 1; 2518191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 2519191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2520195819Srmacklem if (op != NULL) { 2521191783Srmacklem clearok = 0; 2522191783Srmacklem break; 2523191783Srmacklem } 2524191783Srmacklem } 2525191783Srmacklem if (clearok) { 2526191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 2527191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 2528191783Srmacklem clearok = 0; 2529191783Srmacklem break; 2530191783Srmacklem } 2531191783Srmacklem } 2532191783Srmacklem } 2533191783Srmacklem if (clearok) { 2534191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 2535191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 2536191783Srmacklem TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list); 2537191783Srmacklem nfscl_delegcnt--; 2538191783Srmacklem newnfsstats.cldelegates--; 2539191783Srmacklem } 2540191783Srmacklem } 2541191783Srmacklem dp = ndp; 2542191783Srmacklem } 2543191783Srmacklem if (igotlock) 2544191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2545191783Srmacklem NFSUNLOCKCLSTATE(); 2546191783Srmacklem 2547191783Srmacklem /* 2548191783Srmacklem * Delegreturn any delegations cleaned out or recalled. 2549191783Srmacklem */ 2550191783Srmacklem TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) { 2551191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 2552191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); 2553191783Srmacklem TAILQ_REMOVE(&dh, dp, nfsdl_list); 2554191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 2555191783Srmacklem } 2556191783Srmacklem 2557229752Srmacklem SLIST_INIT(&lfh); 2558191783Srmacklem /* 2559229599Srmacklem * Call nfscl_cleanupkext() once per second to check for 2560229599Srmacklem * open/lock owners where the process has exited. 2561191783Srmacklem */ 2562247502Sjhb mytime = NFSD_MONOSEC; 2563247502Sjhb if (prevsec != mytime) { 2564247502Sjhb prevsec = mytime; 2565229752Srmacklem nfscl_cleanupkext(clp, &lfh); 2566191783Srmacklem } 2567191783Srmacklem 2568229752Srmacklem /* 2569229752Srmacklem * Do a ReleaseLockOwner for all lock owners where the 2570229752Srmacklem * associated process no longer exists, as found by 2571229752Srmacklem * nfscl_cleanupkext(). 2572229752Srmacklem */ 2573229752Srmacklem newnfs_setroot(cred); 2574229752Srmacklem SLIST_FOREACH_SAFE(lfhp, &lfh, nfslfh_list, nlfhp) { 2575229752Srmacklem LIST_FOREACH_SAFE(lp, &lfhp->nfslfh_lock, nfsl_list, 2576229752Srmacklem nlp) { 2577229752Srmacklem (void)nfsrpc_rellockown(clp->nfsc_nmp, lp, 2578229752Srmacklem lfhp->nfslfh_fh, lfhp->nfslfh_len, cred, 2579229752Srmacklem p); 2580229752Srmacklem nfscl_freelockowner(lp, 0); 2581229752Srmacklem } 2582229752Srmacklem free(lfhp, M_TEMP); 2583229752Srmacklem } 2584229752Srmacklem SLIST_INIT(&lfh); 2585229752Srmacklem 2586222389Srmacklem NFSLOCKCLSTATE(); 2587191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0) 2588222389Srmacklem (void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl", 2589222389Srmacklem hz); 2590191783Srmacklem if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) { 2591222389Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD; 2592222389Srmacklem NFSUNLOCKCLSTATE(); 2593191783Srmacklem NFSFREECRED(cred); 2594191783Srmacklem wakeup((caddr_t)clp); 2595191783Srmacklem return; 2596191783Srmacklem } 2597222389Srmacklem NFSUNLOCKCLSTATE(); 2598191783Srmacklem } 2599191783Srmacklem} 2600191783Srmacklem 2601191783Srmacklem/* 2602191783Srmacklem * Initiate state recovery. Called when NFSERR_STALECLIENTID or 2603191783Srmacklem * NFSERR_STALESTATEID is received. 2604191783Srmacklem */ 2605191783SrmacklemAPPLESTATIC void 2606191783Srmacklemnfscl_initiate_recovery(struct nfsclclient *clp) 2607191783Srmacklem{ 2608191783Srmacklem 2609191783Srmacklem if (clp == NULL) 2610191783Srmacklem return; 2611191783Srmacklem NFSLOCKCLSTATE(); 2612191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECOVER; 2613191783Srmacklem NFSUNLOCKCLSTATE(); 2614191783Srmacklem wakeup((caddr_t)clp); 2615191783Srmacklem} 2616191783Srmacklem 2617191783Srmacklem/* 2618191783Srmacklem * Dump out the state stuff for debugging. 2619191783Srmacklem */ 2620191783SrmacklemAPPLESTATIC void 2621191783Srmacklemnfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens, 2622191783Srmacklem int lockowner, int locks) 2623191783Srmacklem{ 2624191783Srmacklem struct nfsclclient *clp; 2625191783Srmacklem struct nfsclowner *owp; 2626191783Srmacklem struct nfsclopen *op; 2627191783Srmacklem struct nfscllockowner *lp; 2628191783Srmacklem struct nfscllock *lop; 2629191783Srmacklem struct nfscldeleg *dp; 2630191783Srmacklem 2631191783Srmacklem clp = nmp->nm_clp; 2632191783Srmacklem if (clp == NULL) { 2633191783Srmacklem printf("nfscl dumpstate NULL clp\n"); 2634191783Srmacklem return; 2635191783Srmacklem } 2636191783Srmacklem NFSLOCKCLSTATE(); 2637191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 2638191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 2639191783Srmacklem if (openowner && !LIST_EMPTY(&owp->nfsow_open)) 2640191783Srmacklem printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n", 2641191783Srmacklem owp->nfsow_owner[0], owp->nfsow_owner[1], 2642191783Srmacklem owp->nfsow_owner[2], owp->nfsow_owner[3], 2643191783Srmacklem owp->nfsow_seqid); 2644191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2645191783Srmacklem if (opens) 2646191783Srmacklem printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n", 2647191783Srmacklem op->nfso_stateid.other[0], op->nfso_stateid.other[1], 2648191783Srmacklem op->nfso_stateid.other[2], op->nfso_opencnt, 2649191783Srmacklem op->nfso_fh[12]); 2650191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 2651191783Srmacklem if (lockowner) 2652191783Srmacklem printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n", 2653191783Srmacklem lp->nfsl_owner[0], lp->nfsl_owner[1], 2654191783Srmacklem lp->nfsl_owner[2], lp->nfsl_owner[3], 2655191783Srmacklem lp->nfsl_seqid, 2656191783Srmacklem lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1], 2657191783Srmacklem lp->nfsl_stateid.other[2]); 2658191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 2659191783Srmacklem if (locks) 2660191783Srmacklem#ifdef __FreeBSD__ 2661191783Srmacklem printf("lck typ=%d fst=%ju end=%ju\n", 2662191783Srmacklem lop->nfslo_type, (intmax_t)lop->nfslo_first, 2663191783Srmacklem (intmax_t)lop->nfslo_end); 2664191783Srmacklem#else 2665191783Srmacklem printf("lck typ=%d fst=%qd end=%qd\n", 2666191783Srmacklem lop->nfslo_type, lop->nfslo_first, 2667191783Srmacklem lop->nfslo_end); 2668191783Srmacklem#endif 2669191783Srmacklem } 2670191783Srmacklem } 2671191783Srmacklem } 2672191783Srmacklem } 2673191783Srmacklem } 2674191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2675191783Srmacklem if (openowner && !LIST_EMPTY(&owp->nfsow_open)) 2676191783Srmacklem printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n", 2677191783Srmacklem owp->nfsow_owner[0], owp->nfsow_owner[1], 2678191783Srmacklem owp->nfsow_owner[2], owp->nfsow_owner[3], 2679191783Srmacklem owp->nfsow_seqid); 2680191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2681191783Srmacklem if (opens) 2682191783Srmacklem printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n", 2683191783Srmacklem op->nfso_stateid.other[0], op->nfso_stateid.other[1], 2684191783Srmacklem op->nfso_stateid.other[2], op->nfso_opencnt, 2685191783Srmacklem op->nfso_fh[12]); 2686191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 2687191783Srmacklem if (lockowner) 2688191783Srmacklem printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n", 2689191783Srmacklem lp->nfsl_owner[0], lp->nfsl_owner[1], 2690191783Srmacklem lp->nfsl_owner[2], lp->nfsl_owner[3], 2691191783Srmacklem lp->nfsl_seqid, 2692191783Srmacklem lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1], 2693191783Srmacklem lp->nfsl_stateid.other[2]); 2694191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 2695191783Srmacklem if (locks) 2696191783Srmacklem#ifdef __FreeBSD__ 2697191783Srmacklem printf("lck typ=%d fst=%ju end=%ju\n", 2698191783Srmacklem lop->nfslo_type, (intmax_t)lop->nfslo_first, 2699191783Srmacklem (intmax_t)lop->nfslo_end); 2700191783Srmacklem#else 2701191783Srmacklem printf("lck typ=%d fst=%qd end=%qd\n", 2702191783Srmacklem lop->nfslo_type, lop->nfslo_first, 2703191783Srmacklem lop->nfslo_end); 2704191783Srmacklem#endif 2705191783Srmacklem } 2706191783Srmacklem } 2707191783Srmacklem } 2708191783Srmacklem } 2709191783Srmacklem NFSUNLOCKCLSTATE(); 2710191783Srmacklem} 2711191783Srmacklem 2712191783Srmacklem/* 2713191783Srmacklem * Check for duplicate open owners and opens. 2714191783Srmacklem * (Only used as a diagnostic aid.) 2715191783Srmacklem */ 2716191783SrmacklemAPPLESTATIC void 2717191783Srmacklemnfscl_dupopen(vnode_t vp, int dupopens) 2718191783Srmacklem{ 2719191783Srmacklem struct nfsclclient *clp; 2720191783Srmacklem struct nfsclowner *owp, *owp2; 2721191783Srmacklem struct nfsclopen *op, *op2; 2722191783Srmacklem struct nfsfh *nfhp; 2723191783Srmacklem 2724191783Srmacklem clp = VFSTONFS(vnode_mount(vp))->nm_clp; 2725191783Srmacklem if (clp == NULL) { 2726191783Srmacklem printf("nfscl dupopen NULL clp\n"); 2727191783Srmacklem return; 2728191783Srmacklem } 2729191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 2730191783Srmacklem NFSLOCKCLSTATE(); 2731191783Srmacklem 2732191783Srmacklem /* 2733191783Srmacklem * First, search for duplicate owners. 2734191783Srmacklem * These should never happen! 2735191783Srmacklem */ 2736191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2737191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2738191783Srmacklem if (owp != owp2 && 2739191783Srmacklem !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner, 2740191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 2741191783Srmacklem NFSUNLOCKCLSTATE(); 2742191783Srmacklem printf("DUP OWNER\n"); 2743191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0); 2744191783Srmacklem return; 2745191783Srmacklem } 2746191783Srmacklem } 2747191783Srmacklem } 2748191783Srmacklem 2749191783Srmacklem /* 2750191783Srmacklem * Now, search for duplicate stateids. 2751191783Srmacklem * These shouldn't happen, either. 2752191783Srmacklem */ 2753191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2754191783Srmacklem LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) { 2755191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2756191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2757191783Srmacklem if (op != op2 && 2758191783Srmacklem (op->nfso_stateid.other[0] != 0 || 2759191783Srmacklem op->nfso_stateid.other[1] != 0 || 2760191783Srmacklem op->nfso_stateid.other[2] != 0) && 2761191783Srmacklem op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] && 2762191783Srmacklem op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] && 2763191783Srmacklem op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) { 2764191783Srmacklem NFSUNLOCKCLSTATE(); 2765191783Srmacklem printf("DUP STATEID\n"); 2766191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 2767191783Srmacklem 0); 2768191783Srmacklem return; 2769191783Srmacklem } 2770191783Srmacklem } 2771191783Srmacklem } 2772191783Srmacklem } 2773191783Srmacklem } 2774191783Srmacklem 2775191783Srmacklem /* 2776191783Srmacklem * Now search for duplicate opens. 2777191783Srmacklem * Duplicate opens for the same owner 2778191783Srmacklem * should never occur. Other duplicates are 2779191783Srmacklem * possible and are checked for if "dupopens" 2780191783Srmacklem * is true. 2781191783Srmacklem */ 2782191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2783191783Srmacklem LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) { 2784191783Srmacklem if (nfhp->nfh_len == op2->nfso_fhlen && 2785191783Srmacklem !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) { 2786191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2787191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2788191783Srmacklem if (op != op2 && nfhp->nfh_len == op->nfso_fhlen && 2789191783Srmacklem !NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) && 2790191783Srmacklem (!NFSBCMP(op->nfso_own->nfsow_owner, 2791191783Srmacklem op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) || 2792191783Srmacklem dupopens)) { 2793191783Srmacklem if (!NFSBCMP(op->nfso_own->nfsow_owner, 2794191783Srmacklem op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) { 2795191783Srmacklem NFSUNLOCKCLSTATE(); 2796191783Srmacklem printf("BADDUP OPEN\n"); 2797191783Srmacklem } else { 2798191783Srmacklem NFSUNLOCKCLSTATE(); 2799191783Srmacklem printf("DUP OPEN\n"); 2800191783Srmacklem } 2801191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 2802191783Srmacklem 0, 0); 2803191783Srmacklem return; 2804191783Srmacklem } 2805191783Srmacklem } 2806191783Srmacklem } 2807191783Srmacklem } 2808191783Srmacklem } 2809191783Srmacklem } 2810191783Srmacklem NFSUNLOCKCLSTATE(); 2811191783Srmacklem} 2812191783Srmacklem 2813191783Srmacklem/* 2814191783Srmacklem * During close, find an open that needs to be dereferenced and 2815191783Srmacklem * dereference it. If there are no more opens for this file, 2816195510Srmacklem * log a message to that effect. 2817195510Srmacklem * Opens aren't actually Close'd until VOP_INACTIVE() is performed 2818195510Srmacklem * on the file's vnode. 2819191783Srmacklem * This is the safe way, since it is difficult to identify 2820195510Srmacklem * which open the close is for and I/O can be performed after the 2821195510Srmacklem * close(2) system call when a file is mmap'd. 2822191783Srmacklem * If it returns 0 for success, there will be a referenced 2823195510Srmacklem * clp returned via clpp. 2824191783Srmacklem */ 2825191783SrmacklemAPPLESTATIC int 2826195510Srmacklemnfscl_getclose(vnode_t vp, struct nfsclclient **clpp) 2827191783Srmacklem{ 2828191783Srmacklem struct nfsclclient *clp; 2829195510Srmacklem struct nfsclowner *owp; 2830195510Srmacklem struct nfsclopen *op; 2831191783Srmacklem struct nfscldeleg *dp; 2832191783Srmacklem struct nfsfh *nfhp; 2833195510Srmacklem int error, notdecr; 2834191783Srmacklem 2835192337Srmacklem error = nfscl_getcl(vp, NULL, NULL, &clp); 2836191783Srmacklem if (error) 2837191783Srmacklem return (error); 2838191783Srmacklem *clpp = clp; 2839191783Srmacklem 2840191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 2841191783Srmacklem notdecr = 1; 2842191783Srmacklem NFSLOCKCLSTATE(); 2843191783Srmacklem /* 2844191783Srmacklem * First, look for one under a delegation that was locally issued 2845191783Srmacklem * and just decrement the opencnt for it. Since all my Opens against 2846191783Srmacklem * the server are DENY_NONE, I don't see a problem with hanging 2847191783Srmacklem * onto them. (It is much easier to use one of the extant Opens 2848191783Srmacklem * that I already have on the server when a Delegation is recalled 2849195510Srmacklem * than to do fresh Opens.) Someday, I might need to rethink this, but. 2850191783Srmacklem */ 2851191783Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len); 2852191783Srmacklem if (dp != NULL) { 2853191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 2854191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2855191783Srmacklem if (op != NULL) { 2856191783Srmacklem /* 2857191783Srmacklem * Since a delegation is for a file, there 2858191783Srmacklem * should never be more than one open for 2859191783Srmacklem * each openowner. 2860191783Srmacklem */ 2861191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 2862191783Srmacklem panic("nfscdeleg opens"); 2863191783Srmacklem if (notdecr && op->nfso_opencnt > 0) { 2864191783Srmacklem notdecr = 0; 2865191783Srmacklem op->nfso_opencnt--; 2866191783Srmacklem break; 2867191783Srmacklem } 2868191783Srmacklem } 2869191783Srmacklem } 2870191783Srmacklem } 2871191783Srmacklem 2872191783Srmacklem /* Now process the opens against the server. */ 2873191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2874195510Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2875192337Srmacklem if (op->nfso_fhlen == nfhp->nfh_len && 2876192337Srmacklem !NFSBCMP(op->nfso_fh, nfhp->nfh_fh, 2877192337Srmacklem nfhp->nfh_len)) { 2878192337Srmacklem /* Found an open, decrement cnt if possible */ 2879192337Srmacklem if (notdecr && op->nfso_opencnt > 0) { 2880192337Srmacklem notdecr = 0; 2881192337Srmacklem op->nfso_opencnt--; 2882192337Srmacklem } 2883192337Srmacklem /* 2884195510Srmacklem * There are more opens, so just return. 2885192337Srmacklem */ 2886192337Srmacklem if (op->nfso_opencnt > 0) { 2887192337Srmacklem NFSUNLOCKCLSTATE(); 2888192337Srmacklem return (0); 2889192337Srmacklem } 2890191783Srmacklem } 2891191783Srmacklem } 2892191783Srmacklem } 2893195510Srmacklem NFSUNLOCKCLSTATE(); 2894195510Srmacklem if (notdecr) 2895195510Srmacklem printf("nfscl: never fnd open\n"); 2896195510Srmacklem return (0); 2897195510Srmacklem} 2898191783Srmacklem 2899195510SrmacklemAPPLESTATIC int 2900195510Srmacklemnfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p) 2901195510Srmacklem{ 2902195510Srmacklem struct nfsclclient *clp; 2903195510Srmacklem struct nfsclowner *owp, *nowp; 2904195510Srmacklem struct nfsclopen *op; 2905195510Srmacklem struct nfscldeleg *dp; 2906195510Srmacklem struct nfsfh *nfhp; 2907195510Srmacklem int error; 2908195510Srmacklem 2909195510Srmacklem error = nfscl_getcl(vp, NULL, NULL, &clp); 2910195510Srmacklem if (error) 2911195510Srmacklem return (error); 2912195510Srmacklem *clpp = clp; 2913195510Srmacklem 2914195510Srmacklem nfhp = VTONFS(vp)->n_fhp; 2915195510Srmacklem NFSLOCKCLSTATE(); 2916195510Srmacklem /* 2917195510Srmacklem * First get rid of the local Open structures, which should be no 2918195510Srmacklem * longer in use. 2919195510Srmacklem */ 2920195510Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len); 2921195510Srmacklem if (dp != NULL) { 2922195510Srmacklem LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) { 2923191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2924195510Srmacklem if (op != NULL) { 2925195510Srmacklem KASSERT((op->nfso_opencnt == 0), 2926195510Srmacklem ("nfscl: bad open cnt on deleg")); 2927195510Srmacklem nfscl_freeopen(op, 1); 2928191783Srmacklem } 2929195510Srmacklem nfscl_freeopenowner(owp, 1); 2930191783Srmacklem } 2931195510Srmacklem } 2932195510Srmacklem 2933195510Srmacklem /* Now process the opens against the server. */ 2934195510Srmacklemlookformore: 2935195510Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2936195510Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2937195510Srmacklem while (op != NULL) { 2938195510Srmacklem if (op->nfso_fhlen == nfhp->nfh_len && 2939195510Srmacklem !NFSBCMP(op->nfso_fh, nfhp->nfh_fh, 2940195510Srmacklem nfhp->nfh_len)) { 2941195510Srmacklem /* Found an open, close it. */ 2942195510Srmacklem KASSERT((op->nfso_opencnt == 0), 2943195510Srmacklem ("nfscl: bad open cnt on server")); 2944195510Srmacklem NFSUNLOCKCLSTATE(); 2945195510Srmacklem nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op, 2946195510Srmacklem p); 2947195510Srmacklem NFSLOCKCLSTATE(); 2948195510Srmacklem goto lookformore; 2949191783Srmacklem } 2950195510Srmacklem op = LIST_NEXT(op, nfso_list); 2951191783Srmacklem } 2952191783Srmacklem } 2953191783Srmacklem NFSUNLOCKCLSTATE(); 2954191783Srmacklem return (0); 2955191783Srmacklem} 2956191783Srmacklem 2957191783Srmacklem/* 2958191783Srmacklem * Return all delegations on this client. 2959191783Srmacklem * (Must be called with client sleep lock.) 2960191783Srmacklem */ 2961191783Srmacklemstatic void 2962191783Srmacklemnfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p) 2963191783Srmacklem{ 2964191783Srmacklem struct nfscldeleg *dp, *ndp; 2965191783Srmacklem struct ucred *cred; 2966191783Srmacklem 2967191783Srmacklem cred = newnfs_getcred(); 2968191783Srmacklem TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) { 2969191783Srmacklem nfscl_cleandeleg(dp); 2970191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); 2971191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 2972191783Srmacklem } 2973191783Srmacklem NFSFREECRED(cred); 2974191783Srmacklem} 2975191783Srmacklem 2976191783Srmacklem/* 2977191783Srmacklem * Do a callback RPC. 2978191783Srmacklem */ 2979191783SrmacklemAPPLESTATIC void 2980191783Srmacklemnfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) 2981191783Srmacklem{ 2982191783Srmacklem int i, op; 2983191783Srmacklem u_int32_t *tl; 2984191783Srmacklem struct nfsclclient *clp; 2985191783Srmacklem struct nfscldeleg *dp = NULL; 2986191783Srmacklem int numops, taglen = -1, error = 0, trunc, ret = 0; 2987191783Srmacklem u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident; 2988191783Srmacklem u_char tag[NFSV4_SMALLSTR + 1], *tagstr; 2989191783Srmacklem vnode_t vp = NULL; 2990191783Srmacklem struct nfsnode *np; 2991191783Srmacklem struct vattr va; 2992191783Srmacklem struct nfsfh *nfhp; 2993191783Srmacklem mount_t mp; 2994191783Srmacklem nfsattrbit_t attrbits, rattrbits; 2995191783Srmacklem nfsv4stateid_t stateid; 2996191783Srmacklem 2997191783Srmacklem nfsrvd_rephead(nd); 2998191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 2999191783Srmacklem taglen = fxdr_unsigned(int, *tl); 3000191783Srmacklem if (taglen < 0) { 3001191783Srmacklem error = EBADRPC; 3002191783Srmacklem goto nfsmout; 3003191783Srmacklem } 3004191783Srmacklem if (taglen <= NFSV4_SMALLSTR) 3005191783Srmacklem tagstr = tag; 3006191783Srmacklem else 3007191783Srmacklem tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK); 3008191783Srmacklem error = nfsrv_mtostr(nd, tagstr, taglen); 3009191783Srmacklem if (error) { 3010191783Srmacklem if (taglen > NFSV4_SMALLSTR) 3011191783Srmacklem free(tagstr, M_TEMP); 3012191783Srmacklem taglen = -1; 3013191783Srmacklem goto nfsmout; 3014191783Srmacklem } 3015191783Srmacklem (void) nfsm_strtom(nd, tag, taglen); 3016191783Srmacklem if (taglen > NFSV4_SMALLSTR) { 3017191783Srmacklem free(tagstr, M_TEMP); 3018191783Srmacklem } 3019191783Srmacklem NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED); 3020191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 3021191783Srmacklem minorvers = fxdr_unsigned(u_int32_t, *tl++); 3022191783Srmacklem if (minorvers != NFSV4_MINORVERSION) 3023191783Srmacklem nd->nd_repstat = NFSERR_MINORVERMISMATCH; 3024191783Srmacklem cbident = fxdr_unsigned(u_int32_t, *tl++); 3025191783Srmacklem if (nd->nd_repstat) 3026191783Srmacklem numops = 0; 3027191783Srmacklem else 3028191783Srmacklem numops = fxdr_unsigned(int, *tl); 3029191783Srmacklem /* 3030191783Srmacklem * Loop around doing the sub ops. 3031191783Srmacklem */ 3032191783Srmacklem for (i = 0; i < numops; i++) { 3033191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 3034191783Srmacklem NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED); 3035191783Srmacklem *repp++ = *tl; 3036191783Srmacklem op = fxdr_unsigned(int, *tl); 3037191783Srmacklem if (op < NFSV4OP_CBGETATTR || op > NFSV4OP_CBRECALL) { 3038191783Srmacklem nd->nd_repstat = NFSERR_OPILLEGAL; 3039191783Srmacklem *repp = nfscl_errmap(nd); 3040191783Srmacklem retops++; 3041191783Srmacklem break; 3042191783Srmacklem } 3043191783Srmacklem nd->nd_procnum = op; 3044191783Srmacklem newnfsstats.cbrpccnt[nd->nd_procnum]++; 3045191783Srmacklem switch (op) { 3046191783Srmacklem case NFSV4OP_CBGETATTR: 3047191783Srmacklem clp = NULL; 3048191783Srmacklem error = nfsm_getfh(nd, &nfhp); 3049191783Srmacklem if (!error) 3050191783Srmacklem error = nfsrv_getattrbits(nd, &attrbits, 3051191783Srmacklem NULL, NULL); 3052191783Srmacklem if (!error) { 3053191783Srmacklem mp = nfscl_getmnt(cbident); 3054191783Srmacklem if (mp == NULL) 3055191783Srmacklem error = NFSERR_SERVERFAULT; 3056191783Srmacklem } 3057191783Srmacklem if (!error) { 3058191783Srmacklem dp = NULL; 3059191783Srmacklem NFSLOCKCLSTATE(); 3060191783Srmacklem clp = nfscl_findcl(VFSTONFS(mp)); 3061191783Srmacklem if (clp != NULL) 3062191783Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, 3063191783Srmacklem nfhp->nfh_len); 3064191783Srmacklem NFSUNLOCKCLSTATE(); 3065191783Srmacklem if (dp == NULL) 3066191783Srmacklem error = NFSERR_SERVERFAULT; 3067191783Srmacklem } 3068191783Srmacklem if (!error) { 3069191783Srmacklem ret = nfscl_ngetreopen(mp, nfhp->nfh_fh, 3070191783Srmacklem nfhp->nfh_len, p, &np); 3071191783Srmacklem if (!ret) 3072191783Srmacklem vp = NFSTOV(np); 3073191783Srmacklem } 3074191783Srmacklem if (nfhp != NULL) 3075191783Srmacklem FREE((caddr_t)nfhp, M_NFSFH); 3076191783Srmacklem if (!error) { 3077191783Srmacklem NFSZERO_ATTRBIT(&rattrbits); 3078191783Srmacklem if (NFSISSET_ATTRBIT(&attrbits, 3079191783Srmacklem NFSATTRBIT_SIZE)) { 3080191783Srmacklem if (!ret) 3081191783Srmacklem va.va_size = np->n_size; 3082191783Srmacklem else 3083191783Srmacklem va.va_size = dp->nfsdl_size; 3084191783Srmacklem NFSSETBIT_ATTRBIT(&rattrbits, 3085191783Srmacklem NFSATTRBIT_SIZE); 3086191783Srmacklem } 3087191783Srmacklem if (NFSISSET_ATTRBIT(&attrbits, 3088191783Srmacklem NFSATTRBIT_CHANGE)) { 3089191783Srmacklem va.va_filerev = dp->nfsdl_change; 3090191783Srmacklem if (ret || (np->n_flag & NDELEGMOD)) 3091191783Srmacklem va.va_filerev++; 3092191783Srmacklem NFSSETBIT_ATTRBIT(&rattrbits, 3093191783Srmacklem NFSATTRBIT_CHANGE); 3094191783Srmacklem } 3095220645Srmacklem (void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va, 3096260172Srmacklem NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0, 3097220645Srmacklem (uint64_t)0); 3098191783Srmacklem if (!ret) 3099191783Srmacklem vrele(vp); 3100191783Srmacklem } 3101191783Srmacklem break; 3102191783Srmacklem case NFSV4OP_CBRECALL: 3103191783Srmacklem clp = NULL; 3104191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3105191783Srmacklem NFSX_UNSIGNED); 3106191783Srmacklem stateid.seqid = *tl++; 3107191783Srmacklem NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, 3108191783Srmacklem NFSX_STATEIDOTHER); 3109191783Srmacklem tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 3110191783Srmacklem trunc = fxdr_unsigned(int, *tl); 3111191783Srmacklem error = nfsm_getfh(nd, &nfhp); 3112191783Srmacklem if (!error) { 3113191783Srmacklem mp = nfscl_getmnt(cbident); 3114191783Srmacklem if (mp == NULL) 3115191783Srmacklem error = NFSERR_SERVERFAULT; 3116191783Srmacklem } 3117191783Srmacklem if (!error) { 3118191783Srmacklem NFSLOCKCLSTATE(); 3119191783Srmacklem clp = nfscl_findcl(VFSTONFS(mp)); 3120191783Srmacklem if (clp != NULL) { 3121191783Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, 3122191783Srmacklem nfhp->nfh_len); 3123214406Srmacklem if (dp != NULL && (dp->nfsdl_flags & 3124214406Srmacklem NFSCLDL_DELEGRET) == 0) { 3125191783Srmacklem dp->nfsdl_flags |= 3126191783Srmacklem NFSCLDL_RECALL; 3127191783Srmacklem wakeup((caddr_t)clp); 3128191783Srmacklem } 3129191783Srmacklem } else { 3130191783Srmacklem error = NFSERR_SERVERFAULT; 3131191783Srmacklem } 3132191783Srmacklem NFSUNLOCKCLSTATE(); 3133191783Srmacklem } 3134191783Srmacklem if (nfhp != NULL) 3135191783Srmacklem FREE((caddr_t)nfhp, M_NFSFH); 3136191783Srmacklem break; 3137191783Srmacklem }; 3138191783Srmacklem if (error) { 3139191783Srmacklem if (error == EBADRPC || error == NFSERR_BADXDR) { 3140191783Srmacklem nd->nd_repstat = NFSERR_BADXDR; 3141191783Srmacklem } else { 3142191783Srmacklem nd->nd_repstat = error; 3143191783Srmacklem } 3144191783Srmacklem error = 0; 3145191783Srmacklem } 3146191783Srmacklem retops++; 3147191783Srmacklem if (nd->nd_repstat) { 3148191783Srmacklem *repp = nfscl_errmap(nd); 3149191783Srmacklem break; 3150191783Srmacklem } else 3151191783Srmacklem *repp = 0; /* NFS4_OK */ 3152191783Srmacklem } 3153191783Srmacklemnfsmout: 3154191783Srmacklem if (error) { 3155191783Srmacklem if (error == EBADRPC || error == NFSERR_BADXDR) 3156191783Srmacklem nd->nd_repstat = NFSERR_BADXDR; 3157191783Srmacklem else 3158191783Srmacklem printf("nfsv4 comperr1=%d\n", error); 3159191783Srmacklem } 3160191783Srmacklem if (taglen == -1) { 3161191783Srmacklem NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 3162191783Srmacklem *tl++ = 0; 3163191783Srmacklem *tl = 0; 3164191783Srmacklem } else { 3165191783Srmacklem *retopsp = txdr_unsigned(retops); 3166191783Srmacklem } 3167191783Srmacklem *nd->nd_errp = nfscl_errmap(nd); 3168191783Srmacklem} 3169191783Srmacklem 3170191783Srmacklem/* 3171191783Srmacklem * Generate the next cbident value. Basically just increment a static value 3172191783Srmacklem * and then check that it isn't already in the list, if it has wrapped around. 3173191783Srmacklem */ 3174191783Srmacklemstatic u_int32_t 3175191783Srmacklemnfscl_nextcbident(void) 3176191783Srmacklem{ 3177191783Srmacklem struct nfsclclient *clp; 3178191783Srmacklem int matched; 3179191783Srmacklem static u_int32_t nextcbident = 0; 3180191783Srmacklem static int haswrapped = 0; 3181191783Srmacklem 3182191783Srmacklem nextcbident++; 3183191783Srmacklem if (nextcbident == 0) 3184191783Srmacklem haswrapped = 1; 3185191783Srmacklem if (haswrapped) { 3186191783Srmacklem /* 3187191783Srmacklem * Search the clientid list for one already using this cbident. 3188191783Srmacklem */ 3189191783Srmacklem do { 3190191783Srmacklem matched = 0; 3191191783Srmacklem NFSLOCKCLSTATE(); 3192191783Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) { 3193191783Srmacklem if (clp->nfsc_cbident == nextcbident) { 3194191783Srmacklem matched = 1; 3195191783Srmacklem break; 3196191783Srmacklem } 3197191783Srmacklem } 3198191783Srmacklem NFSUNLOCKCLSTATE(); 3199191783Srmacklem if (matched == 1) 3200191783Srmacklem nextcbident++; 3201191783Srmacklem } while (matched); 3202191783Srmacklem } 3203191783Srmacklem return (nextcbident); 3204191783Srmacklem} 3205191783Srmacklem 3206191783Srmacklem/* 3207191783Srmacklem * Get the mount point related to a given cbident. 3208191783Srmacklem */ 3209191783Srmacklemstatic mount_t 3210191783Srmacklemnfscl_getmnt(u_int32_t cbident) 3211191783Srmacklem{ 3212191783Srmacklem struct nfsclclient *clp; 3213191783Srmacklem struct nfsmount *nmp; 3214191783Srmacklem 3215191783Srmacklem NFSLOCKCLSTATE(); 3216191783Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) { 3217191783Srmacklem if (clp->nfsc_cbident == cbident) 3218191783Srmacklem break; 3219191783Srmacklem } 3220191783Srmacklem if (clp == NULL) { 3221191783Srmacklem NFSUNLOCKCLSTATE(); 3222191783Srmacklem return (NULL); 3223191783Srmacklem } 3224191783Srmacklem nmp = clp->nfsc_nmp; 3225191783Srmacklem NFSUNLOCKCLSTATE(); 3226191783Srmacklem return (nmp->nm_mountp); 3227191783Srmacklem} 3228191783Srmacklem 3229191783Srmacklem/* 3230191783Srmacklem * Search for a lock conflict locally on the client. A conflict occurs if 3231191783Srmacklem * - not same owner and overlapping byte range and at least one of them is 3232191783Srmacklem * a write lock or this is an unlock. 3233191783Srmacklem */ 3234191783Srmacklemstatic int 3235201439Srmacklemnfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen, 3236201439Srmacklem struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp, 3237201439Srmacklem struct nfscllock **lopp) 3238191783Srmacklem{ 3239191783Srmacklem struct nfsclowner *owp; 3240191783Srmacklem struct nfsclopen *op; 3241191783Srmacklem int ret; 3242191783Srmacklem 3243191783Srmacklem if (dp != NULL) { 3244191783Srmacklem ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp); 3245191783Srmacklem if (ret) 3246191783Srmacklem return (ret); 3247191783Srmacklem } 3248191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 3249191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 3250201439Srmacklem if (op->nfso_fhlen == fhlen && 3251201439Srmacklem !NFSBCMP(op->nfso_fh, fhp, fhlen)) { 3252201439Srmacklem ret = nfscl_checkconflict(&op->nfso_lock, nlop, 3253201439Srmacklem own, lopp); 3254201439Srmacklem if (ret) 3255201439Srmacklem return (ret); 3256201439Srmacklem } 3257191783Srmacklem } 3258191783Srmacklem } 3259191783Srmacklem return (0); 3260191783Srmacklem} 3261191783Srmacklem 3262191783Srmacklemstatic int 3263191783Srmacklemnfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop, 3264191783Srmacklem u_int8_t *own, struct nfscllock **lopp) 3265191783Srmacklem{ 3266191783Srmacklem struct nfscllockowner *lp; 3267191783Srmacklem struct nfscllock *lop; 3268191783Srmacklem 3269191783Srmacklem LIST_FOREACH(lp, lhp, nfsl_list) { 3270191783Srmacklem if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) { 3271191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 3272191783Srmacklem if (lop->nfslo_first >= nlop->nfslo_end) 3273191783Srmacklem break; 3274191783Srmacklem if (lop->nfslo_end <= nlop->nfslo_first) 3275191783Srmacklem continue; 3276191783Srmacklem if (lop->nfslo_type == F_WRLCK || 3277191783Srmacklem nlop->nfslo_type == F_WRLCK || 3278191783Srmacklem nlop->nfslo_type == F_UNLCK) { 3279191783Srmacklem if (lopp != NULL) 3280191783Srmacklem *lopp = lop; 3281191783Srmacklem return (NFSERR_DENIED); 3282191783Srmacklem } 3283191783Srmacklem } 3284191783Srmacklem } 3285191783Srmacklem } 3286191783Srmacklem return (0); 3287191783Srmacklem} 3288191783Srmacklem 3289191783Srmacklem/* 3290191783Srmacklem * Check for a local conflicting lock. 3291191783Srmacklem */ 3292191783SrmacklemAPPLESTATIC int 3293191783Srmacklemnfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off, 3294222719Srmacklem u_int64_t len, struct flock *fl, NFSPROC_T *p, void *id, int flags) 3295191783Srmacklem{ 3296191783Srmacklem struct nfscllock *lop, nlck; 3297191783Srmacklem struct nfscldeleg *dp; 3298191783Srmacklem struct nfsnode *np; 3299191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 3300191783Srmacklem int error; 3301191783Srmacklem 3302191783Srmacklem nlck.nfslo_type = fl->l_type; 3303191783Srmacklem nlck.nfslo_first = off; 3304191783Srmacklem if (len == NFS64BITSSET) { 3305191783Srmacklem nlck.nfslo_end = NFS64BITSSET; 3306191783Srmacklem } else { 3307191783Srmacklem nlck.nfslo_end = off + len; 3308191783Srmacklem if (nlck.nfslo_end <= nlck.nfslo_first) 3309191783Srmacklem return (NFSERR_INVAL); 3310191783Srmacklem } 3311191783Srmacklem np = VTONFS(vp); 3312222719Srmacklem nfscl_filllockowner(id, own, flags); 3313191783Srmacklem NFSLOCKCLSTATE(); 3314191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 3315201439Srmacklem error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 3316201439Srmacklem &nlck, own, dp, &lop); 3317201439Srmacklem if (error != 0) { 3318191783Srmacklem fl->l_whence = SEEK_SET; 3319191783Srmacklem fl->l_start = lop->nfslo_first; 3320191783Srmacklem if (lop->nfslo_end == NFS64BITSSET) 3321191783Srmacklem fl->l_len = 0; 3322191783Srmacklem else 3323191783Srmacklem fl->l_len = lop->nfslo_end - lop->nfslo_first; 3324191783Srmacklem fl->l_pid = (pid_t)0; 3325191783Srmacklem fl->l_type = lop->nfslo_type; 3326201439Srmacklem error = -1; /* no RPC required */ 3327191783Srmacklem } else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) || 3328191783Srmacklem fl->l_type == F_RDLCK)) { 3329191783Srmacklem /* 3330191783Srmacklem * The delegation ensures that there isn't a conflicting 3331191783Srmacklem * lock on the server, so return -1 to indicate an RPC 3332191783Srmacklem * isn't required. 3333191783Srmacklem */ 3334191783Srmacklem fl->l_type = F_UNLCK; 3335191783Srmacklem error = -1; 3336191783Srmacklem } 3337191783Srmacklem NFSUNLOCKCLSTATE(); 3338191783Srmacklem return (error); 3339191783Srmacklem} 3340191783Srmacklem 3341191783Srmacklem/* 3342191783Srmacklem * Handle Recall of a delegation. 3343191783Srmacklem * The clp must be exclusive locked when this is called. 3344191783Srmacklem */ 3345191783Srmacklemstatic int 3346191783Srmacklemnfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp, 3347207082Srmacklem struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p, 3348207082Srmacklem int called_from_renewthread) 3349191783Srmacklem{ 3350191783Srmacklem struct nfsclowner *owp, *lowp, *nowp; 3351191783Srmacklem struct nfsclopen *op, *lop; 3352191783Srmacklem struct nfscllockowner *lp; 3353191783Srmacklem struct nfscllock *lckp; 3354191783Srmacklem struct nfsnode *np; 3355191783Srmacklem int error = 0, ret, gotvp = 0; 3356191783Srmacklem 3357191783Srmacklem if (vp == NULL) { 3358191783Srmacklem /* 3359191783Srmacklem * First, get a vnode for the file. This is needed to do RPCs. 3360191783Srmacklem */ 3361191783Srmacklem ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh, 3362191783Srmacklem dp->nfsdl_fhlen, p, &np); 3363191783Srmacklem if (ret) { 3364191783Srmacklem /* 3365191783Srmacklem * File isn't open, so nothing to move over to the 3366191783Srmacklem * server. 3367191783Srmacklem */ 3368191783Srmacklem return (0); 3369191783Srmacklem } 3370191783Srmacklem vp = NFSTOV(np); 3371191783Srmacklem gotvp = 1; 3372191783Srmacklem } else { 3373191783Srmacklem np = VTONFS(vp); 3374191783Srmacklem } 3375191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET; 3376191783Srmacklem 3377191783Srmacklem /* 3378191783Srmacklem * Ok, if it's a write delegation, flush data to the server, so 3379191783Srmacklem * that close/open consistency is retained. 3380191783Srmacklem */ 3381207082Srmacklem ret = 0; 3382191783Srmacklem NFSLOCKNODE(np); 3383191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) { 3384191783Srmacklem np->n_flag |= NDELEGRECALL; 3385191783Srmacklem NFSUNLOCKNODE(np); 3386207082Srmacklem ret = ncl_flush(vp, MNT_WAIT, cred, p, 1, 3387207082Srmacklem called_from_renewthread); 3388191783Srmacklem NFSLOCKNODE(np); 3389214406Srmacklem np->n_flag &= ~NDELEGRECALL; 3390191783Srmacklem } 3391214406Srmacklem NFSINVALATTRCACHE(np); 3392191783Srmacklem NFSUNLOCKNODE(np); 3393207082Srmacklem if (ret == EIO && called_from_renewthread != 0) { 3394207082Srmacklem /* 3395207082Srmacklem * If the flush failed with EIO for the renew thread, 3396207082Srmacklem * return now, so that the dirty buffer will be flushed 3397207082Srmacklem * later. 3398207082Srmacklem */ 3399207082Srmacklem if (gotvp != 0) 3400207082Srmacklem vrele(vp); 3401207082Srmacklem return (ret); 3402207082Srmacklem } 3403191783Srmacklem 3404191783Srmacklem /* 3405191783Srmacklem * Now, for each openowner with opens issued locally, move them 3406191783Srmacklem * over to state against the server. 3407191783Srmacklem */ 3408191783Srmacklem LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) { 3409191783Srmacklem lop = LIST_FIRST(&lowp->nfsow_open); 3410191783Srmacklem if (lop != NULL) { 3411191783Srmacklem if (LIST_NEXT(lop, nfso_list) != NULL) 3412191783Srmacklem panic("nfsdlg mult opens"); 3413191783Srmacklem /* 3414191783Srmacklem * Look for the same openowner against the server. 3415191783Srmacklem */ 3416191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 3417191783Srmacklem if (!NFSBCMP(lowp->nfsow_owner, 3418191783Srmacklem owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) { 3419191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3420191783Srmacklem ret = nfscl_moveopen(vp, clp, nmp, lop, 3421191783Srmacklem owp, dp, cred, p); 3422191783Srmacklem if (ret == NFSERR_STALECLIENTID || 3423191783Srmacklem ret == NFSERR_STALEDONTRECOVER) { 3424191783Srmacklem if (gotvp) 3425191783Srmacklem vrele(vp); 3426191783Srmacklem return (ret); 3427191783Srmacklem } 3428191783Srmacklem if (ret) { 3429191783Srmacklem nfscl_freeopen(lop, 1); 3430191783Srmacklem if (!error) 3431191783Srmacklem error = ret; 3432191783Srmacklem } 3433191783Srmacklem break; 3434191783Srmacklem } 3435191783Srmacklem } 3436191783Srmacklem 3437191783Srmacklem /* 3438191783Srmacklem * If no openowner found, create one and get an open 3439191783Srmacklem * for it. 3440191783Srmacklem */ 3441191783Srmacklem if (owp == NULL) { 3442191783Srmacklem MALLOC(nowp, struct nfsclowner *, 3443191783Srmacklem sizeof (struct nfsclowner), M_NFSCLOWNER, 3444191783Srmacklem M_WAITOK); 3445191783Srmacklem nfscl_newopen(clp, NULL, &owp, &nowp, &op, 3446191783Srmacklem NULL, lowp->nfsow_owner, dp->nfsdl_fh, 3447191783Srmacklem dp->nfsdl_fhlen, NULL); 3448191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3449191783Srmacklem ret = nfscl_moveopen(vp, clp, nmp, lop, 3450191783Srmacklem owp, dp, cred, p); 3451191783Srmacklem if (ret) { 3452191783Srmacklem nfscl_freeopenowner(owp, 0); 3453191783Srmacklem if (ret == NFSERR_STALECLIENTID || 3454191783Srmacklem ret == NFSERR_STALEDONTRECOVER) { 3455191783Srmacklem if (gotvp) 3456191783Srmacklem vrele(vp); 3457191783Srmacklem return (ret); 3458191783Srmacklem } 3459191783Srmacklem if (ret) { 3460191783Srmacklem nfscl_freeopen(lop, 1); 3461191783Srmacklem if (!error) 3462191783Srmacklem error = ret; 3463191783Srmacklem } 3464191783Srmacklem } 3465191783Srmacklem } 3466191783Srmacklem } 3467191783Srmacklem } 3468191783Srmacklem 3469191783Srmacklem /* 3470191783Srmacklem * Now, get byte range locks for any locks done locally. 3471191783Srmacklem */ 3472191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 3473191783Srmacklem LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) { 3474191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3475191783Srmacklem ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p); 3476191783Srmacklem if (ret == NFSERR_STALESTATEID || 3477191783Srmacklem ret == NFSERR_STALEDONTRECOVER || 3478191783Srmacklem ret == NFSERR_STALECLIENTID) { 3479191783Srmacklem if (gotvp) 3480191783Srmacklem vrele(vp); 3481191783Srmacklem return (ret); 3482191783Srmacklem } 3483191783Srmacklem if (ret && !error) 3484191783Srmacklem error = ret; 3485191783Srmacklem } 3486191783Srmacklem } 3487191783Srmacklem if (gotvp) 3488191783Srmacklem vrele(vp); 3489191783Srmacklem return (error); 3490191783Srmacklem} 3491191783Srmacklem 3492191783Srmacklem/* 3493191783Srmacklem * Move a locally issued open over to an owner on the state list. 3494191783Srmacklem * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and 3495191783Srmacklem * returns with it unlocked. 3496191783Srmacklem */ 3497191783Srmacklemstatic int 3498191783Srmacklemnfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp, 3499191783Srmacklem struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp, 3500191783Srmacklem struct ucred *cred, NFSPROC_T *p) 3501191783Srmacklem{ 3502191783Srmacklem struct nfsclopen *op, *nop; 3503191783Srmacklem struct nfscldeleg *ndp; 3504191783Srmacklem struct nfsnode *np; 3505191783Srmacklem int error = 0, newone; 3506191783Srmacklem 3507191783Srmacklem /* 3508191783Srmacklem * First, look for an appropriate open, If found, just increment the 3509191783Srmacklem * opencnt in it. 3510191783Srmacklem */ 3511191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 3512191783Srmacklem if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode && 3513191783Srmacklem op->nfso_fhlen == lop->nfso_fhlen && 3514191783Srmacklem !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) { 3515191783Srmacklem op->nfso_opencnt += lop->nfso_opencnt; 3516191783Srmacklem nfscl_freeopen(lop, 1); 3517191783Srmacklem return (0); 3518191783Srmacklem } 3519191783Srmacklem } 3520191783Srmacklem 3521191783Srmacklem /* No appropriate open, so we have to do one against the server. */ 3522191783Srmacklem np = VTONFS(vp); 3523191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 3524191783Srmacklem lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK); 3525191783Srmacklem newone = 0; 3526191783Srmacklem nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner, 3527191783Srmacklem lop->nfso_fh, lop->nfso_fhlen, &newone); 3528191783Srmacklem ndp = dp; 3529191783Srmacklem error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen, 3530191783Srmacklem lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op, 3531191783Srmacklem NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p); 3532191783Srmacklem if (error) { 3533191783Srmacklem if (newone) 3534191783Srmacklem nfscl_freeopen(op, 0); 3535191783Srmacklem } else { 3536191783Srmacklem if (newone) 3537191783Srmacklem newnfs_copyincred(cred, &op->nfso_cred); 3538191783Srmacklem op->nfso_mode |= lop->nfso_mode; 3539191783Srmacklem op->nfso_opencnt += lop->nfso_opencnt; 3540191783Srmacklem nfscl_freeopen(lop, 1); 3541191783Srmacklem } 3542191783Srmacklem if (nop != NULL) 3543191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 3544191783Srmacklem if (ndp != NULL) { 3545191783Srmacklem /* 3546191783Srmacklem * What should I do with the returned delegation, since the 3547191783Srmacklem * delegation is being recalled? For now, just printf and 3548191783Srmacklem * through it away. 3549191783Srmacklem */ 3550191783Srmacklem printf("Moveopen returned deleg\n"); 3551191783Srmacklem FREE((caddr_t)ndp, M_NFSCLDELEG); 3552191783Srmacklem } 3553191783Srmacklem return (error); 3554191783Srmacklem} 3555191783Srmacklem 3556191783Srmacklem/* 3557191783Srmacklem * Recall all delegations on this client. 3558191783Srmacklem */ 3559191783Srmacklemstatic void 3560191783Srmacklemnfscl_totalrecall(struct nfsclclient *clp) 3561191783Srmacklem{ 3562191783Srmacklem struct nfscldeleg *dp; 3563191783Srmacklem 3564214406Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 3565214406Srmacklem if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0) 3566214406Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 3567214406Srmacklem } 3568191783Srmacklem} 3569191783Srmacklem 3570191783Srmacklem/* 3571191783Srmacklem * Relock byte ranges. Called for delegation recall and state expiry. 3572191783Srmacklem */ 3573191783Srmacklemstatic int 3574191783Srmacklemnfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp, 3575191783Srmacklem struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred, 3576191783Srmacklem NFSPROC_T *p) 3577191783Srmacklem{ 3578191783Srmacklem struct nfscllockowner *nlp; 3579191783Srmacklem struct nfsfh *nfhp; 3580191783Srmacklem u_int64_t off, len; 3581191783Srmacklem u_int32_t clidrev = 0; 3582191783Srmacklem int error, newone, donelocally; 3583191783Srmacklem 3584191783Srmacklem off = lop->nfslo_first; 3585191783Srmacklem len = lop->nfslo_end - lop->nfslo_first; 3586191783Srmacklem error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p, 3587229752Srmacklem clp, 1, NULL, lp->nfsl_lockflags, lp->nfsl_owner, 3588229752Srmacklem lp->nfsl_openowner, &nlp, &newone, &donelocally); 3589191783Srmacklem if (error || donelocally) 3590191783Srmacklem return (error); 3591191783Srmacklem if (nmp->nm_clp != NULL) 3592191783Srmacklem clidrev = nmp->nm_clp->nfsc_clientidrev; 3593191783Srmacklem else 3594191783Srmacklem clidrev = 0; 3595191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 3596191783Srmacklem error = nfscl_trylock(nmp, vp, nfhp->nfh_fh, 3597191783Srmacklem nfhp->nfh_len, nlp, newone, 0, off, 3598191783Srmacklem len, lop->nfslo_type, cred, p); 3599191783Srmacklem if (error) 3600191783Srmacklem nfscl_freelockowner(nlp, 0); 3601191783Srmacklem return (error); 3602191783Srmacklem} 3603191783Srmacklem 3604191783Srmacklem/* 3605191783Srmacklem * Called to re-open a file. Basically get a vnode for the file handle 3606191783Srmacklem * and then call nfsrpc_openrpc() to do the rest. 3607191783Srmacklem */ 3608191783Srmacklemstatic int 3609191783Srmacklemnfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, 3610191783Srmacklem u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp, 3611191783Srmacklem struct ucred *cred, NFSPROC_T *p) 3612191783Srmacklem{ 3613191783Srmacklem struct nfsnode *np; 3614191783Srmacklem vnode_t vp; 3615191783Srmacklem int error; 3616191783Srmacklem 3617191783Srmacklem error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np); 3618191783Srmacklem if (error) 3619191783Srmacklem return (error); 3620191783Srmacklem vp = NFSTOV(np); 3621191783Srmacklem if (np->n_v4 != NULL) { 3622191783Srmacklem error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, 3623191783Srmacklem np->n_v4->n4_fhlen, fhp, fhlen, mode, op, 3624191783Srmacklem NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0, 3625191783Srmacklem cred, p); 3626191783Srmacklem } else { 3627191783Srmacklem error = EINVAL; 3628191783Srmacklem } 3629191783Srmacklem vrele(vp); 3630191783Srmacklem return (error); 3631191783Srmacklem} 3632191783Srmacklem 3633191783Srmacklem/* 3634191783Srmacklem * Try an open against the server. Just call nfsrpc_openrpc(), retrying while 3635191783Srmacklem * NFSERR_DELAY. Also, try system credentials, if the passed in credentials 3636191783Srmacklem * fail. 3637191783Srmacklem */ 3638191783Srmacklemstatic int 3639191783Srmacklemnfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen, 3640191783Srmacklem u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op, 3641191783Srmacklem u_int8_t *name, int namelen, struct nfscldeleg **ndpp, 3642191783Srmacklem int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p) 3643191783Srmacklem{ 3644191783Srmacklem int error; 3645191783Srmacklem 3646191783Srmacklem do { 3647191783Srmacklem error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen, 3648191783Srmacklem mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p, 3649191783Srmacklem 0, 0); 3650191783Srmacklem if (error == NFSERR_DELAY) 3651207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstryop"); 3652191783Srmacklem } while (error == NFSERR_DELAY); 3653191783Srmacklem if (error == EAUTH || error == EACCES) { 3654191783Srmacklem /* Try again using system credentials */ 3655191783Srmacklem newnfs_setroot(cred); 3656191783Srmacklem do { 3657191783Srmacklem error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, 3658191783Srmacklem newfhlen, mode, op, name, namelen, ndpp, reclaim, 3659191783Srmacklem delegtype, cred, p, 1, 0); 3660191783Srmacklem if (error == NFSERR_DELAY) 3661207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstryop"); 3662191783Srmacklem } while (error == NFSERR_DELAY); 3663191783Srmacklem } 3664191783Srmacklem return (error); 3665191783Srmacklem} 3666191783Srmacklem 3667191783Srmacklem/* 3668191783Srmacklem * Try a byte range lock. Just loop on nfsrpc_lock() while it returns 3669191783Srmacklem * NFSERR_DELAY. Also, retry with system credentials, if the provided 3670191783Srmacklem * cred don't work. 3671191783Srmacklem */ 3672191783Srmacklemstatic int 3673191783Srmacklemnfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, 3674191783Srmacklem int fhlen, struct nfscllockowner *nlp, int newone, int reclaim, 3675191783Srmacklem u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p) 3676191783Srmacklem{ 3677191783Srmacklem struct nfsrv_descript nfsd, *nd = &nfsd; 3678191783Srmacklem int error; 3679191783Srmacklem 3680191783Srmacklem do { 3681191783Srmacklem error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone, 3682191783Srmacklem reclaim, off, len, type, cred, p, 0); 3683191783Srmacklem if (!error && nd->nd_repstat == NFSERR_DELAY) 3684207170Srmacklem (void) nfs_catnap(PZERO, (int)nd->nd_repstat, 3685207170Srmacklem "nfstrylck"); 3686191783Srmacklem } while (!error && nd->nd_repstat == NFSERR_DELAY); 3687191783Srmacklem if (!error) 3688191783Srmacklem error = nd->nd_repstat; 3689191783Srmacklem if (error == EAUTH || error == EACCES) { 3690191783Srmacklem /* Try again using root credentials */ 3691191783Srmacklem newnfs_setroot(cred); 3692191783Srmacklem do { 3693191783Srmacklem error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, 3694191783Srmacklem newone, reclaim, off, len, type, cred, p, 1); 3695191783Srmacklem if (!error && nd->nd_repstat == NFSERR_DELAY) 3696207170Srmacklem (void) nfs_catnap(PZERO, (int)nd->nd_repstat, 3697207170Srmacklem "nfstrylck"); 3698191783Srmacklem } while (!error && nd->nd_repstat == NFSERR_DELAY); 3699191783Srmacklem if (!error) 3700191783Srmacklem error = nd->nd_repstat; 3701191783Srmacklem } 3702191783Srmacklem return (error); 3703191783Srmacklem} 3704191783Srmacklem 3705191783Srmacklem/* 3706191783Srmacklem * Try a delegreturn against the server. Just call nfsrpc_delegreturn(), 3707191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in 3708191783Srmacklem * credentials fail. 3709191783Srmacklem */ 3710191783Srmacklemstatic int 3711191783Srmacklemnfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred, 3712191783Srmacklem struct nfsmount *nmp, NFSPROC_T *p) 3713191783Srmacklem{ 3714191783Srmacklem int error; 3715191783Srmacklem 3716191783Srmacklem do { 3717191783Srmacklem error = nfsrpc_delegreturn(dp, cred, nmp, p, 0); 3718191783Srmacklem if (error == NFSERR_DELAY) 3719207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstrydp"); 3720191783Srmacklem } while (error == NFSERR_DELAY); 3721191783Srmacklem if (error == EAUTH || error == EACCES) { 3722191783Srmacklem /* Try again using system credentials */ 3723191783Srmacklem newnfs_setroot(cred); 3724191783Srmacklem do { 3725191783Srmacklem error = nfsrpc_delegreturn(dp, cred, nmp, p, 1); 3726191783Srmacklem if (error == NFSERR_DELAY) 3727207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstrydp"); 3728191783Srmacklem } while (error == NFSERR_DELAY); 3729191783Srmacklem } 3730191783Srmacklem return (error); 3731191783Srmacklem} 3732191783Srmacklem 3733191783Srmacklem/* 3734191783Srmacklem * Try a close against the server. Just call nfsrpc_closerpc(), 3735191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in 3736191783Srmacklem * credentials fail. 3737191783Srmacklem */ 3738191783SrmacklemAPPLESTATIC int 3739191783Srmacklemnfscl_tryclose(struct nfsclopen *op, struct ucred *cred, 3740191783Srmacklem struct nfsmount *nmp, NFSPROC_T *p) 3741191783Srmacklem{ 3742191783Srmacklem struct nfsrv_descript nfsd, *nd = &nfsd; 3743191783Srmacklem int error; 3744191783Srmacklem 3745191783Srmacklem do { 3746191783Srmacklem error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0); 3747191783Srmacklem if (error == NFSERR_DELAY) 3748207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstrycl"); 3749191783Srmacklem } while (error == NFSERR_DELAY); 3750191783Srmacklem if (error == EAUTH || error == EACCES) { 3751191783Srmacklem /* Try again using system credentials */ 3752191783Srmacklem newnfs_setroot(cred); 3753191783Srmacklem do { 3754191783Srmacklem error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1); 3755191783Srmacklem if (error == NFSERR_DELAY) 3756207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstrycl"); 3757191783Srmacklem } while (error == NFSERR_DELAY); 3758191783Srmacklem } 3759191783Srmacklem return (error); 3760191783Srmacklem} 3761191783Srmacklem 3762191783Srmacklem/* 3763191783Srmacklem * Decide if a delegation on a file permits close without flushing writes 3764191783Srmacklem * to the server. This might be a big performance win in some environments. 3765191783Srmacklem * (Not useful until the client does caching on local stable storage.) 3766191783Srmacklem */ 3767210786SrmacklemAPPLESTATIC int 3768191783Srmacklemnfscl_mustflush(vnode_t vp) 3769191783Srmacklem{ 3770191783Srmacklem struct nfsclclient *clp; 3771191783Srmacklem struct nfscldeleg *dp; 3772191783Srmacklem struct nfsnode *np; 3773191783Srmacklem struct nfsmount *nmp; 3774191783Srmacklem 3775191783Srmacklem np = VTONFS(vp); 3776191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 3777191783Srmacklem if (!NFSHASNFSV4(nmp)) 3778210786Srmacklem return (1); 3779191783Srmacklem NFSLOCKCLSTATE(); 3780191783Srmacklem clp = nfscl_findcl(nmp); 3781191783Srmacklem if (clp == NULL) { 3782191783Srmacklem NFSUNLOCKCLSTATE(); 3783210786Srmacklem return (1); 3784191783Srmacklem } 3785191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 3786214406Srmacklem if (dp != NULL && (dp->nfsdl_flags & 3787214406Srmacklem (NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 3788214406Srmacklem NFSCLDL_WRITE && 3789191783Srmacklem (dp->nfsdl_sizelimit >= np->n_size || 3790191783Srmacklem !NFSHASSTRICT3530(nmp))) { 3791191783Srmacklem NFSUNLOCKCLSTATE(); 3792210786Srmacklem return (0); 3793191783Srmacklem } 3794191783Srmacklem NFSUNLOCKCLSTATE(); 3795210786Srmacklem return (1); 3796191783Srmacklem} 3797191783Srmacklem 3798191783Srmacklem/* 3799191783Srmacklem * See if a (write) delegation exists for this file. 3800191783Srmacklem */ 3801191783SrmacklemAPPLESTATIC int 3802191783Srmacklemnfscl_nodeleg(vnode_t vp, int writedeleg) 3803191783Srmacklem{ 3804191783Srmacklem struct nfsclclient *clp; 3805191783Srmacklem struct nfscldeleg *dp; 3806191783Srmacklem struct nfsnode *np; 3807191783Srmacklem struct nfsmount *nmp; 3808191783Srmacklem 3809191783Srmacklem np = VTONFS(vp); 3810191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 3811191783Srmacklem if (!NFSHASNFSV4(nmp)) 3812191783Srmacklem return (1); 3813191783Srmacklem NFSLOCKCLSTATE(); 3814191783Srmacklem clp = nfscl_findcl(nmp); 3815191783Srmacklem if (clp == NULL) { 3816191783Srmacklem NFSUNLOCKCLSTATE(); 3817191783Srmacklem return (1); 3818191783Srmacklem } 3819191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 3820214406Srmacklem if (dp != NULL && 3821214406Srmacklem (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 && 3822214406Srmacklem (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) == 3823214406Srmacklem NFSCLDL_WRITE)) { 3824191783Srmacklem NFSUNLOCKCLSTATE(); 3825191783Srmacklem return (0); 3826191783Srmacklem } 3827191783Srmacklem NFSUNLOCKCLSTATE(); 3828191783Srmacklem return (1); 3829191783Srmacklem} 3830191783Srmacklem 3831191783Srmacklem/* 3832191783Srmacklem * Look for an associated delegation that should be DelegReturned. 3833191783Srmacklem */ 3834191783SrmacklemAPPLESTATIC int 3835191783Srmacklemnfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp) 3836191783Srmacklem{ 3837191783Srmacklem struct nfsclclient *clp; 3838191783Srmacklem struct nfscldeleg *dp; 3839191783Srmacklem struct nfsclowner *owp; 3840191783Srmacklem struct nfscllockowner *lp; 3841191783Srmacklem struct nfsmount *nmp; 3842191783Srmacklem struct ucred *cred; 3843191783Srmacklem struct nfsnode *np; 3844191783Srmacklem int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept; 3845191783Srmacklem 3846191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 3847191783Srmacklem np = VTONFS(vp); 3848191783Srmacklem NFSLOCKCLSTATE(); 3849191783Srmacklem /* 3850191783Srmacklem * Loop around waiting for: 3851191783Srmacklem * - outstanding I/O operations on delegations to complete 3852191783Srmacklem * - for a delegation on vp that has state, lock the client and 3853191783Srmacklem * do a recall 3854191783Srmacklem * - return delegation with no state 3855191783Srmacklem */ 3856191783Srmacklem while (1) { 3857191783Srmacklem clp = nfscl_findcl(nmp); 3858191783Srmacklem if (clp == NULL) { 3859191783Srmacklem NFSUNLOCKCLSTATE(); 3860191783Srmacklem return (retcnt); 3861191783Srmacklem } 3862191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 3863191783Srmacklem np->n_fhp->nfh_len); 3864191783Srmacklem if (dp != NULL) { 3865191783Srmacklem /* 3866191783Srmacklem * Wait for outstanding I/O ops to be done. 3867191783Srmacklem */ 3868191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 3869191783Srmacklem if (igotlock) { 3870191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3871191783Srmacklem igotlock = 0; 3872191783Srmacklem } 3873191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 3874191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 3875191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 3876191783Srmacklem continue; 3877191783Srmacklem } 3878191783Srmacklem needsrecall = 0; 3879191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 3880191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 3881191783Srmacklem needsrecall = 1; 3882191783Srmacklem break; 3883191783Srmacklem } 3884191783Srmacklem } 3885191783Srmacklem if (!needsrecall) { 3886191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 3887191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 3888191783Srmacklem needsrecall = 1; 3889191783Srmacklem break; 3890191783Srmacklem } 3891191783Srmacklem } 3892191783Srmacklem } 3893191783Srmacklem if (needsrecall && !triedrecall) { 3894214406Srmacklem dp->nfsdl_flags |= NFSCLDL_DELEGRET; 3895191783Srmacklem islept = 0; 3896191783Srmacklem while (!igotlock) { 3897191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 3898222389Srmacklem &islept, NFSCLSTATEMUTEXPTR, NULL); 3899191783Srmacklem if (islept) 3900191783Srmacklem break; 3901191783Srmacklem } 3902191783Srmacklem if (islept) 3903191783Srmacklem continue; 3904191783Srmacklem NFSUNLOCKCLSTATE(); 3905191783Srmacklem cred = newnfs_getcred(); 3906191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3907207082Srmacklem (void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0); 3908191783Srmacklem NFSFREECRED(cred); 3909191783Srmacklem triedrecall = 1; 3910191783Srmacklem NFSLOCKCLSTATE(); 3911191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3912191783Srmacklem igotlock = 0; 3913191783Srmacklem continue; 3914191783Srmacklem } 3915191783Srmacklem *stp = dp->nfsdl_stateid; 3916191783Srmacklem retcnt = 1; 3917191783Srmacklem nfscl_cleandeleg(dp); 3918191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 3919191783Srmacklem } 3920191783Srmacklem if (igotlock) 3921191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3922191783Srmacklem NFSUNLOCKCLSTATE(); 3923191783Srmacklem return (retcnt); 3924191783Srmacklem } 3925191783Srmacklem} 3926191783Srmacklem 3927191783Srmacklem/* 3928191783Srmacklem * Look for associated delegation(s) that should be DelegReturned. 3929191783Srmacklem */ 3930191783SrmacklemAPPLESTATIC int 3931191783Srmacklemnfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp, 3932191783Srmacklem nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p) 3933191783Srmacklem{ 3934191783Srmacklem struct nfsclclient *clp; 3935191783Srmacklem struct nfscldeleg *dp; 3936191783Srmacklem struct nfsclowner *owp; 3937191783Srmacklem struct nfscllockowner *lp; 3938191783Srmacklem struct nfsmount *nmp; 3939191783Srmacklem struct ucred *cred; 3940191783Srmacklem struct nfsnode *np; 3941191783Srmacklem int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept; 3942191783Srmacklem 3943191783Srmacklem nmp = VFSTONFS(vnode_mount(fvp)); 3944191783Srmacklem *gotfdp = 0; 3945191783Srmacklem *gottdp = 0; 3946191783Srmacklem NFSLOCKCLSTATE(); 3947191783Srmacklem /* 3948191783Srmacklem * Loop around waiting for: 3949191783Srmacklem * - outstanding I/O operations on delegations to complete 3950191783Srmacklem * - for a delegation on fvp that has state, lock the client and 3951191783Srmacklem * do a recall 3952191783Srmacklem * - return delegation(s) with no state. 3953191783Srmacklem */ 3954191783Srmacklem while (1) { 3955191783Srmacklem clp = nfscl_findcl(nmp); 3956191783Srmacklem if (clp == NULL) { 3957191783Srmacklem NFSUNLOCKCLSTATE(); 3958191783Srmacklem return (retcnt); 3959191783Srmacklem } 3960191783Srmacklem np = VTONFS(fvp); 3961191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 3962191783Srmacklem np->n_fhp->nfh_len); 3963191783Srmacklem if (dp != NULL && *gotfdp == 0) { 3964191783Srmacklem /* 3965191783Srmacklem * Wait for outstanding I/O ops to be done. 3966191783Srmacklem */ 3967191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 3968191783Srmacklem if (igotlock) { 3969191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 3970191783Srmacklem igotlock = 0; 3971191783Srmacklem } 3972191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 3973191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 3974191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 3975191783Srmacklem continue; 3976191783Srmacklem } 3977191783Srmacklem needsrecall = 0; 3978191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 3979191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 3980191783Srmacklem needsrecall = 1; 3981191783Srmacklem break; 3982191783Srmacklem } 3983191783Srmacklem } 3984191783Srmacklem if (!needsrecall) { 3985191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 3986191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 3987191783Srmacklem needsrecall = 1; 3988191783Srmacklem break; 3989191783Srmacklem } 3990191783Srmacklem } 3991191783Srmacklem } 3992191783Srmacklem if (needsrecall && !triedrecall) { 3993214406Srmacklem dp->nfsdl_flags |= NFSCLDL_DELEGRET; 3994191783Srmacklem islept = 0; 3995191783Srmacklem while (!igotlock) { 3996191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 3997222389Srmacklem &islept, NFSCLSTATEMUTEXPTR, NULL); 3998191783Srmacklem if (islept) 3999191783Srmacklem break; 4000191783Srmacklem } 4001191783Srmacklem if (islept) 4002191783Srmacklem continue; 4003191783Srmacklem NFSUNLOCKCLSTATE(); 4004191783Srmacklem cred = newnfs_getcred(); 4005191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 4006207082Srmacklem (void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0); 4007191783Srmacklem NFSFREECRED(cred); 4008191783Srmacklem triedrecall = 1; 4009191783Srmacklem NFSLOCKCLSTATE(); 4010191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 4011191783Srmacklem igotlock = 0; 4012191783Srmacklem continue; 4013191783Srmacklem } 4014191783Srmacklem *fstp = dp->nfsdl_stateid; 4015191783Srmacklem retcnt++; 4016191783Srmacklem *gotfdp = 1; 4017191783Srmacklem nfscl_cleandeleg(dp); 4018191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 4019191783Srmacklem } 4020191783Srmacklem if (igotlock) { 4021191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 4022191783Srmacklem igotlock = 0; 4023191783Srmacklem } 4024191783Srmacklem if (tvp != NULL) { 4025191783Srmacklem np = VTONFS(tvp); 4026191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 4027191783Srmacklem np->n_fhp->nfh_len); 4028191783Srmacklem if (dp != NULL && *gottdp == 0) { 4029191783Srmacklem /* 4030191783Srmacklem * Wait for outstanding I/O ops to be done. 4031191783Srmacklem */ 4032191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 4033191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 4034191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 4035191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 4036191783Srmacklem continue; 4037191783Srmacklem } 4038191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 4039191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 4040191783Srmacklem NFSUNLOCKCLSTATE(); 4041191783Srmacklem return (retcnt); 4042191783Srmacklem } 4043191783Srmacklem } 4044191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 4045191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 4046191783Srmacklem NFSUNLOCKCLSTATE(); 4047191783Srmacklem return (retcnt); 4048191783Srmacklem } 4049191783Srmacklem } 4050191783Srmacklem *tstp = dp->nfsdl_stateid; 4051191783Srmacklem retcnt++; 4052191783Srmacklem *gottdp = 1; 4053191783Srmacklem nfscl_cleandeleg(dp); 4054191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 4055191783Srmacklem } 4056191783Srmacklem } 4057191783Srmacklem NFSUNLOCKCLSTATE(); 4058191783Srmacklem return (retcnt); 4059191783Srmacklem } 4060191783Srmacklem} 4061191783Srmacklem 4062191783Srmacklem/* 4063191783Srmacklem * Get a reference on the clientid associated with the mount point. 4064191783Srmacklem * Return 1 if success, 0 otherwise. 4065191783Srmacklem */ 4066191783SrmacklemAPPLESTATIC int 4067191783Srmacklemnfscl_getref(struct nfsmount *nmp) 4068191783Srmacklem{ 4069191783Srmacklem struct nfsclclient *clp; 4070191783Srmacklem 4071191783Srmacklem NFSLOCKCLSTATE(); 4072191783Srmacklem clp = nfscl_findcl(nmp); 4073191783Srmacklem if (clp == NULL) { 4074191783Srmacklem NFSUNLOCKCLSTATE(); 4075191783Srmacklem return (0); 4076191783Srmacklem } 4077222389Srmacklem nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL); 4078191783Srmacklem NFSUNLOCKCLSTATE(); 4079191783Srmacklem return (1); 4080191783Srmacklem} 4081191783Srmacklem 4082191783Srmacklem/* 4083191783Srmacklem * Release a reference on a clientid acquired with the above call. 4084191783Srmacklem */ 4085191783SrmacklemAPPLESTATIC void 4086191783Srmacklemnfscl_relref(struct nfsmount *nmp) 4087191783Srmacklem{ 4088191783Srmacklem struct nfsclclient *clp; 4089191783Srmacklem 4090191783Srmacklem NFSLOCKCLSTATE(); 4091191783Srmacklem clp = nfscl_findcl(nmp); 4092191783Srmacklem if (clp == NULL) { 4093191783Srmacklem NFSUNLOCKCLSTATE(); 4094191783Srmacklem return; 4095191783Srmacklem } 4096191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 4097191783Srmacklem NFSUNLOCKCLSTATE(); 4098191783Srmacklem} 4099191783Srmacklem 4100191783Srmacklem/* 4101191783Srmacklem * Save the size attribute in the delegation, since the nfsnode 4102191783Srmacklem * is going away. 4103191783Srmacklem */ 4104191783SrmacklemAPPLESTATIC void 4105191783Srmacklemnfscl_reclaimnode(vnode_t vp) 4106191783Srmacklem{ 4107191783Srmacklem struct nfsclclient *clp; 4108191783Srmacklem struct nfscldeleg *dp; 4109191783Srmacklem struct nfsnode *np = VTONFS(vp); 4110191783Srmacklem struct nfsmount *nmp; 4111191783Srmacklem 4112191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4113191783Srmacklem if (!NFSHASNFSV4(nmp)) 4114191783Srmacklem return; 4115191783Srmacklem NFSLOCKCLSTATE(); 4116191783Srmacklem clp = nfscl_findcl(nmp); 4117191783Srmacklem if (clp == NULL) { 4118191783Srmacklem NFSUNLOCKCLSTATE(); 4119191783Srmacklem return; 4120191783Srmacklem } 4121191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4122191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) 4123191783Srmacklem dp->nfsdl_size = np->n_size; 4124191783Srmacklem NFSUNLOCKCLSTATE(); 4125191783Srmacklem} 4126191783Srmacklem 4127191783Srmacklem/* 4128191783Srmacklem * Get the saved size attribute in the delegation, since it is a 4129191783Srmacklem * newly allocated nfsnode. 4130191783Srmacklem */ 4131191783SrmacklemAPPLESTATIC void 4132191783Srmacklemnfscl_newnode(vnode_t vp) 4133191783Srmacklem{ 4134191783Srmacklem struct nfsclclient *clp; 4135191783Srmacklem struct nfscldeleg *dp; 4136191783Srmacklem struct nfsnode *np = VTONFS(vp); 4137191783Srmacklem struct nfsmount *nmp; 4138191783Srmacklem 4139191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4140191783Srmacklem if (!NFSHASNFSV4(nmp)) 4141191783Srmacklem return; 4142191783Srmacklem NFSLOCKCLSTATE(); 4143191783Srmacklem clp = nfscl_findcl(nmp); 4144191783Srmacklem if (clp == NULL) { 4145191783Srmacklem NFSUNLOCKCLSTATE(); 4146191783Srmacklem return; 4147191783Srmacklem } 4148191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4149191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) 4150191783Srmacklem np->n_size = dp->nfsdl_size; 4151191783Srmacklem NFSUNLOCKCLSTATE(); 4152191783Srmacklem} 4153191783Srmacklem 4154191783Srmacklem/* 4155191783Srmacklem * If there is a valid write delegation for this file, set the modtime 4156191783Srmacklem * to the local clock time. 4157191783Srmacklem */ 4158191783SrmacklemAPPLESTATIC void 4159191783Srmacklemnfscl_delegmodtime(vnode_t vp) 4160191783Srmacklem{ 4161191783Srmacklem struct nfsclclient *clp; 4162191783Srmacklem struct nfscldeleg *dp; 4163191783Srmacklem struct nfsnode *np = VTONFS(vp); 4164191783Srmacklem struct nfsmount *nmp; 4165191783Srmacklem 4166191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4167191783Srmacklem if (!NFSHASNFSV4(nmp)) 4168191783Srmacklem return; 4169191783Srmacklem NFSLOCKCLSTATE(); 4170191783Srmacklem clp = nfscl_findcl(nmp); 4171191783Srmacklem if (clp == NULL) { 4172191783Srmacklem NFSUNLOCKCLSTATE(); 4173191783Srmacklem return; 4174191783Srmacklem } 4175191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4176191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) { 4177247502Sjhb nanotime(&dp->nfsdl_modtime); 4178191783Srmacklem dp->nfsdl_flags |= NFSCLDL_MODTIMESET; 4179191783Srmacklem } 4180191783Srmacklem NFSUNLOCKCLSTATE(); 4181191783Srmacklem} 4182191783Srmacklem 4183191783Srmacklem/* 4184191783Srmacklem * If there is a valid write delegation for this file with a modtime set, 4185191783Srmacklem * put that modtime in mtime. 4186191783Srmacklem */ 4187191783SrmacklemAPPLESTATIC void 4188191783Srmacklemnfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime) 4189191783Srmacklem{ 4190191783Srmacklem struct nfsclclient *clp; 4191191783Srmacklem struct nfscldeleg *dp; 4192191783Srmacklem struct nfsnode *np = VTONFS(vp); 4193191783Srmacklem struct nfsmount *nmp; 4194191783Srmacklem 4195191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4196191783Srmacklem if (!NFSHASNFSV4(nmp)) 4197191783Srmacklem return; 4198191783Srmacklem NFSLOCKCLSTATE(); 4199191783Srmacklem clp = nfscl_findcl(nmp); 4200191783Srmacklem if (clp == NULL) { 4201191783Srmacklem NFSUNLOCKCLSTATE(); 4202191783Srmacklem return; 4203191783Srmacklem } 4204191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4205191783Srmacklem if (dp != NULL && 4206191783Srmacklem (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) == 4207191783Srmacklem (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) 4208191783Srmacklem *mtime = dp->nfsdl_modtime; 4209191783Srmacklem NFSUNLOCKCLSTATE(); 4210191783Srmacklem} 4211191783Srmacklem 4212191783Srmacklemstatic int 4213191783Srmacklemnfscl_errmap(struct nfsrv_descript *nd) 4214191783Srmacklem{ 4215191783Srmacklem short *defaulterrp, *errp; 4216191783Srmacklem 4217191783Srmacklem if (!nd->nd_repstat) 4218191783Srmacklem return (0); 4219191783Srmacklem if (nd->nd_procnum == NFSPROC_NOOP) 4220191783Srmacklem return (txdr_unsigned(nd->nd_repstat & 0xffff)); 4221191783Srmacklem if (nd->nd_repstat == EBADRPC) 4222191783Srmacklem return (txdr_unsigned(NFSERR_BADXDR)); 4223191783Srmacklem if (nd->nd_repstat == NFSERR_MINORVERMISMATCH || 4224191783Srmacklem nd->nd_repstat == NFSERR_OPILLEGAL) 4225191783Srmacklem return (txdr_unsigned(nd->nd_repstat)); 4226191783Srmacklem errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum]; 4227191783Srmacklem while (*++errp) 4228191783Srmacklem if (*errp == (short)nd->nd_repstat) 4229191783Srmacklem return (txdr_unsigned(nd->nd_repstat)); 4230191783Srmacklem return (txdr_unsigned(*defaulterrp)); 4231191783Srmacklem} 4232191783Srmacklem 4233