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. 67227744Srmacklem * - The function nfscl_cleanup_common() is executed after a process exits. 68227744Srmacklem * 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; 89244042Srmacklemextern u_int32_t newnfs_false, newnfs_true; 90244042Srmacklemextern int nfscl_debuglevel; 91191783SrmacklemNFSREQSPINLOCK; 92191783SrmacklemNFSCLSTATEMUTEX; 93191783Srmacklemint nfscl_inited = 0; 94191783Srmacklemstruct nfsclhead nfsclhead; /* Head of clientid list */ 95191783Srmacklemint nfscl_deleghighwater = NFSCLDELEGHIGHWATER; 96244042Srmacklemint nfscl_layouthighwater = NFSCLLAYOUTHIGHWATER; 97191783Srmacklem#endif /* !APPLEKEXT */ 98191783Srmacklem 99191783Srmacklemstatic int nfscl_delegcnt = 0; 100244042Srmacklemstatic int nfscl_layoutcnt = 0; 101191783Srmacklemstatic int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *, 102223774Srmacklem u_int8_t *, u_int32_t, struct nfscllockowner **, struct nfsclopen **); 103191783Srmacklemstatic void nfscl_clrelease(struct nfsclclient *); 104191783Srmacklemstatic void nfscl_cleanclient(struct nfsclclient *); 105191783Srmacklemstatic void nfscl_expireclient(struct nfsclclient *, struct nfsmount *, 106191783Srmacklem struct ucred *, NFSPROC_T *); 107191783Srmacklemstatic int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *, 108191783Srmacklem struct nfsmount *, struct ucred *, NFSPROC_T *); 109191783Srmacklemstatic void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *); 110191783Srmacklemstatic void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *, 111191783Srmacklem struct nfscllock *, int); 112191783Srmacklemstatic int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **, 113191783Srmacklem struct nfscllock **, int); 114191783Srmacklemstatic void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *); 115191783Srmacklemstatic u_int32_t nfscl_nextcbident(void); 116244042Srmacklemstatic mount_t nfscl_getmnt(int, uint8_t *, u_int32_t, struct nfsclclient **); 117244042Srmacklemstatic struct nfsclclient *nfscl_getclnt(u_int32_t); 118244042Srmacklemstatic struct nfsclclient *nfscl_getclntsess(uint8_t *); 119191783Srmacklemstatic struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *, 120191783Srmacklem int); 121244042Srmacklemstatic void nfscl_retoncloselayout(struct nfsclclient *, uint8_t *, int); 122244042Srmacklemstatic void nfscl_reldevinfo_locked(struct nfscldevinfo *); 123244042Srmacklemstatic struct nfscllayout *nfscl_findlayout(struct nfsclclient *, u_int8_t *, 124244042Srmacklem int); 125244042Srmacklemstatic struct nfscldevinfo *nfscl_finddevinfo(struct nfsclclient *, uint8_t *); 126191783Srmacklemstatic int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *, 127191783Srmacklem u_int8_t *, struct nfscllock **); 128191783Srmacklemstatic void nfscl_freealllocks(struct nfscllockownerhead *, int); 129201439Srmacklemstatic int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int, 130201439Srmacklem struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **); 131191783Srmacklemstatic void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *, 132191783Srmacklem struct nfsclowner **, struct nfsclowner **, struct nfsclopen **, 133191783Srmacklem struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *); 134191783Srmacklemstatic int nfscl_moveopen(vnode_t , struct nfsclclient *, 135191783Srmacklem struct nfsmount *, struct nfsclopen *, struct nfsclowner *, 136191783Srmacklem struct nfscldeleg *, struct ucred *, NFSPROC_T *); 137191783Srmacklemstatic void nfscl_totalrecall(struct nfsclclient *); 138191783Srmacklemstatic int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *, 139191783Srmacklem struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *); 140191783Srmacklemstatic int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int, 141191783Srmacklem u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int, 142191783Srmacklem struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *); 143191783Srmacklemstatic int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *, 144191783Srmacklem int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short, 145191783Srmacklem struct ucred *, NFSPROC_T *); 146191783Srmacklemstatic int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t, 147191783Srmacklem struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *); 148191783Srmacklemstatic void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *); 149191783Srmacklemstatic int nfscl_errmap(struct nfsrv_descript *); 150191783Srmacklemstatic void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *); 151191783Srmacklemstatic int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *, 152207082Srmacklem struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int); 153191783Srmacklemstatic void nfscl_freeopenowner(struct nfsclowner *, int); 154191783Srmacklemstatic void nfscl_cleandeleg(struct nfscldeleg *); 155191783Srmacklemstatic int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *, 156191783Srmacklem struct nfsmount *, NFSPROC_T *); 157228217Srmacklemstatic void nfscl_emptylockowner(struct nfscllockowner *, 158228217Srmacklem struct nfscllockownerfhhead *); 159244042Srmacklemstatic void nfscl_mergeflayouts(struct nfsclflayouthead *, 160244042Srmacklem struct nfsclflayouthead *); 161244042Srmacklemstatic int nfscl_layoutrecall(int, struct nfscllayout *, uint32_t, uint64_t, 162244042Srmacklem uint64_t, uint32_t, struct nfsclrecalllayout *); 163244042Srmacklemstatic int nfscl_seq(uint32_t, uint32_t); 164244042Srmacklemstatic void nfscl_layoutreturn(struct nfsmount *, struct nfscllayout *, 165244042Srmacklem struct ucred *, NFSPROC_T *); 166244042Srmacklemstatic void nfscl_dolayoutcommit(struct nfsmount *, struct nfscllayout *, 167244042Srmacklem struct ucred *, NFSPROC_T *); 168191783Srmacklem 169191783Srmacklemstatic short nfscberr_null[] = { 170191783Srmacklem 0, 171191783Srmacklem 0, 172191783Srmacklem}; 173191783Srmacklem 174191783Srmacklemstatic short nfscberr_getattr[] = { 175191783Srmacklem NFSERR_RESOURCE, 176191783Srmacklem NFSERR_BADHANDLE, 177191783Srmacklem NFSERR_BADXDR, 178191783Srmacklem NFSERR_RESOURCE, 179191783Srmacklem NFSERR_SERVERFAULT, 180191783Srmacklem 0, 181191783Srmacklem}; 182191783Srmacklem 183191783Srmacklemstatic short nfscberr_recall[] = { 184191783Srmacklem NFSERR_RESOURCE, 185191783Srmacklem NFSERR_BADHANDLE, 186191783Srmacklem NFSERR_BADSTATEID, 187191783Srmacklem NFSERR_BADXDR, 188191783Srmacklem NFSERR_RESOURCE, 189191783Srmacklem NFSERR_SERVERFAULT, 190191783Srmacklem 0, 191191783Srmacklem}; 192191783Srmacklem 193191783Srmacklemstatic short *nfscl_cberrmap[] = { 194191783Srmacklem nfscberr_null, 195191783Srmacklem nfscberr_null, 196191783Srmacklem nfscberr_null, 197191783Srmacklem nfscberr_getattr, 198191783Srmacklem nfscberr_recall 199191783Srmacklem}; 200191783Srmacklem 201191783Srmacklem#define NETFAMILY(clp) \ 202191783Srmacklem (((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET) 203191783Srmacklem 204191783Srmacklem/* 205191783Srmacklem * Called for an open operation. 206191783Srmacklem * If the nfhp argument is NULL, just get an openowner. 207191783Srmacklem */ 208191783SrmacklemAPPLESTATIC int 209191783Srmacklemnfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg, 210191783Srmacklem struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp, 211191783Srmacklem struct nfsclopen **opp, int *newonep, int *retp, int lockit) 212191783Srmacklem{ 213191783Srmacklem struct nfsclclient *clp; 214191783Srmacklem struct nfsclowner *owp, *nowp; 215191783Srmacklem struct nfsclopen *op = NULL, *nop = NULL; 216191783Srmacklem struct nfscldeleg *dp; 217191783Srmacklem struct nfsclownerhead *ohp; 218191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 219191783Srmacklem int ret; 220191783Srmacklem 221191783Srmacklem if (newonep != NULL) 222191783Srmacklem *newonep = 0; 223191783Srmacklem if (opp != NULL) 224191783Srmacklem *opp = NULL; 225191783Srmacklem if (owpp != NULL) 226191783Srmacklem *owpp = NULL; 227191783Srmacklem 228191783Srmacklem /* 229191783Srmacklem * Might need one or both of these, so MALLOC them now, to 230191783Srmacklem * avoid a tsleep() in MALLOC later. 231191783Srmacklem */ 232191783Srmacklem MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner), 233191783Srmacklem M_NFSCLOWNER, M_WAITOK); 234191783Srmacklem if (nfhp != NULL) 235191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 236191783Srmacklem fhlen - 1, M_NFSCLOPEN, M_WAITOK); 237244042Srmacklem ret = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp); 238191783Srmacklem if (ret != 0) { 239191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 240191783Srmacklem if (nop != NULL) 241191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 242191783Srmacklem return (ret); 243191783Srmacklem } 244191783Srmacklem 245191783Srmacklem /* 246191783Srmacklem * Get the Open iff it already exists. 247191783Srmacklem * If none found, add the new one or return error, depending upon 248191783Srmacklem * "create". 249191783Srmacklem */ 250222719Srmacklem nfscl_filllockowner(p->td_proc, own, F_POSIX); 251191783Srmacklem NFSLOCKCLSTATE(); 252191783Srmacklem dp = NULL; 253191783Srmacklem /* First check the delegation list */ 254191783Srmacklem if (nfhp != NULL && usedeleg) { 255191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) { 256191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 257191783Srmacklem !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) { 258191783Srmacklem if (!(amode & NFSV4OPEN_ACCESSWRITE) || 259191783Srmacklem (dp->nfsdl_flags & NFSCLDL_WRITE)) 260191783Srmacklem break; 261191783Srmacklem dp = NULL; 262191783Srmacklem break; 263191783Srmacklem } 264191783Srmacklem } 265191783Srmacklem } 266191783Srmacklem 267191783Srmacklem if (dp != NULL) 268191783Srmacklem ohp = &dp->nfsdl_owner; 269191783Srmacklem else 270191783Srmacklem ohp = &clp->nfsc_owner; 271191783Srmacklem /* Now, search for an openowner */ 272191783Srmacklem LIST_FOREACH(owp, ohp, nfsow_list) { 273191783Srmacklem if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN)) 274191783Srmacklem break; 275191783Srmacklem } 276191783Srmacklem 277191783Srmacklem /* 278191783Srmacklem * Create a new open, as required. 279191783Srmacklem */ 280191783Srmacklem nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen, 281191783Srmacklem newonep); 282191783Srmacklem 283191783Srmacklem /* 284191783Srmacklem * Serialize modifications to the open owner for multiple threads 285191783Srmacklem * within the same process using a read/write sleep lock. 286191783Srmacklem */ 287191783Srmacklem if (lockit) 288191783Srmacklem nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR); 289191783Srmacklem NFSUNLOCKCLSTATE(); 290191783Srmacklem if (nowp != NULL) 291191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 292191783Srmacklem if (nop != NULL) 293191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 294191783Srmacklem if (owpp != NULL) 295191783Srmacklem *owpp = owp; 296191783Srmacklem if (opp != NULL) 297191783Srmacklem *opp = op; 298206688Srmacklem if (retp != NULL) { 299206688Srmacklem if (nfhp != NULL && dp != NULL && nop == NULL) 300206688Srmacklem /* new local open on delegation */ 301206688Srmacklem *retp = NFSCLOPEN_SETCRED; 302206688Srmacklem else 303206688Srmacklem *retp = NFSCLOPEN_OK; 304206688Srmacklem } 305191783Srmacklem 306191783Srmacklem /* 307191783Srmacklem * Now, check the mode on the open and return the appropriate 308191783Srmacklem * value. 309191783Srmacklem */ 310191783Srmacklem if (op != NULL && (amode & ~(op->nfso_mode))) { 311191783Srmacklem op->nfso_mode |= amode; 312191783Srmacklem if (retp != NULL && dp == NULL) 313191783Srmacklem *retp = NFSCLOPEN_DOOPEN; 314191783Srmacklem } 315191783Srmacklem return (0); 316191783Srmacklem} 317191783Srmacklem 318191783Srmacklem/* 319191783Srmacklem * Create a new open, as required. 320191783Srmacklem */ 321191783Srmacklemstatic void 322191783Srmacklemnfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp, 323191783Srmacklem struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp, 324191783Srmacklem struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen, 325191783Srmacklem int *newonep) 326191783Srmacklem{ 327191783Srmacklem struct nfsclowner *owp = *owpp, *nowp; 328191783Srmacklem struct nfsclopen *op, *nop; 329191783Srmacklem 330191783Srmacklem if (nowpp != NULL) 331191783Srmacklem nowp = *nowpp; 332191783Srmacklem else 333191783Srmacklem nowp = NULL; 334191783Srmacklem if (nopp != NULL) 335191783Srmacklem nop = *nopp; 336191783Srmacklem else 337191783Srmacklem nop = NULL; 338191783Srmacklem if (owp == NULL && nowp != NULL) { 339191783Srmacklem NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN); 340191783Srmacklem LIST_INIT(&nowp->nfsow_open); 341191783Srmacklem nowp->nfsow_clp = clp; 342191783Srmacklem nowp->nfsow_seqid = 0; 343191783Srmacklem nowp->nfsow_defunct = 0; 344191783Srmacklem nfscl_lockinit(&nowp->nfsow_rwlock); 345191783Srmacklem if (dp != NULL) { 346191783Srmacklem newnfsstats.cllocalopenowners++; 347191783Srmacklem LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list); 348191783Srmacklem } else { 349191783Srmacklem newnfsstats.clopenowners++; 350191783Srmacklem LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list); 351191783Srmacklem } 352191783Srmacklem owp = *owpp = nowp; 353191783Srmacklem *nowpp = NULL; 354191783Srmacklem if (newonep != NULL) 355191783Srmacklem *newonep = 1; 356191783Srmacklem } 357191783Srmacklem 358191783Srmacklem /* If an fhp has been specified, create an Open as well. */ 359191783Srmacklem if (fhp != NULL) { 360191783Srmacklem /* and look for the correct open, based upon FH */ 361191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 362191783Srmacklem if (op->nfso_fhlen == fhlen && 363191783Srmacklem !NFSBCMP(op->nfso_fh, fhp, fhlen)) 364191783Srmacklem break; 365191783Srmacklem } 366191783Srmacklem if (op == NULL && nop != NULL) { 367191783Srmacklem nop->nfso_own = owp; 368191783Srmacklem nop->nfso_mode = 0; 369191783Srmacklem nop->nfso_opencnt = 0; 370191783Srmacklem nop->nfso_posixlock = 1; 371191783Srmacklem nop->nfso_fhlen = fhlen; 372191783Srmacklem NFSBCOPY(fhp, nop->nfso_fh, fhlen); 373191783Srmacklem LIST_INIT(&nop->nfso_lock); 374191783Srmacklem nop->nfso_stateid.seqid = 0; 375191783Srmacklem nop->nfso_stateid.other[0] = 0; 376191783Srmacklem nop->nfso_stateid.other[1] = 0; 377191783Srmacklem nop->nfso_stateid.other[2] = 0; 378191783Srmacklem if (dp != NULL) { 379191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 380191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, 381191783Srmacklem nfsdl_list); 382191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 383191783Srmacklem newnfsstats.cllocalopens++; 384191783Srmacklem } else { 385191783Srmacklem newnfsstats.clopens++; 386191783Srmacklem } 387191783Srmacklem LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list); 388191783Srmacklem *opp = nop; 389191783Srmacklem *nopp = NULL; 390191783Srmacklem if (newonep != NULL) 391191783Srmacklem *newonep = 1; 392191783Srmacklem } else { 393191783Srmacklem *opp = op; 394191783Srmacklem } 395191783Srmacklem } 396191783Srmacklem} 397191783Srmacklem 398191783Srmacklem/* 399191783Srmacklem * Called to find/add a delegation to a client. 400191783Srmacklem */ 401191783SrmacklemAPPLESTATIC int 402191783Srmacklemnfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp, 403191783Srmacklem int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp) 404191783Srmacklem{ 405191783Srmacklem struct nfscldeleg *dp = *dpp, *tdp; 406191783Srmacklem 407191783Srmacklem /* 408191783Srmacklem * First, if we have received a Read delegation for a file on a 409191783Srmacklem * read/write file system, just return it, because they aren't 410191783Srmacklem * useful, imho. 411191783Srmacklem */ 412191783Srmacklem if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) && 413191783Srmacklem (dp->nfsdl_flags & NFSCLDL_READ)) { 414191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p); 415191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 416191783Srmacklem *dpp = NULL; 417191783Srmacklem return (0); 418191783Srmacklem } 419191783Srmacklem 420191783Srmacklem /* Look for the correct deleg, based upon FH */ 421191783Srmacklem NFSLOCKCLSTATE(); 422191783Srmacklem tdp = nfscl_finddeleg(clp, nfhp, fhlen); 423191783Srmacklem if (tdp == NULL) { 424191783Srmacklem if (dp == NULL) { 425191783Srmacklem NFSUNLOCKCLSTATE(); 426191783Srmacklem return (NFSERR_BADSTATEID); 427191783Srmacklem } 428191783Srmacklem *dpp = NULL; 429191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list); 430191783Srmacklem LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp, 431191783Srmacklem nfsdl_hash); 432191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 433191783Srmacklem newnfsstats.cldelegates++; 434191783Srmacklem nfscl_delegcnt++; 435191783Srmacklem } else { 436191783Srmacklem /* 437191783Srmacklem * Delegation already exists, what do we do if a new one?? 438191783Srmacklem */ 439191783Srmacklem if (dp != NULL) { 440191783Srmacklem printf("Deleg already exists!\n"); 441191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 442191783Srmacklem *dpp = NULL; 443191783Srmacklem } else { 444191783Srmacklem *dpp = tdp; 445191783Srmacklem } 446191783Srmacklem } 447191783Srmacklem NFSUNLOCKCLSTATE(); 448191783Srmacklem return (0); 449191783Srmacklem} 450191783Srmacklem 451191783Srmacklem/* 452191783Srmacklem * Find a delegation for this file handle. Return NULL upon failure. 453191783Srmacklem */ 454191783Srmacklemstatic struct nfscldeleg * 455191783Srmacklemnfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen) 456191783Srmacklem{ 457191783Srmacklem struct nfscldeleg *dp; 458191783Srmacklem 459191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) { 460191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 461191783Srmacklem !NFSBCMP(dp->nfsdl_fh, fhp, fhlen)) 462191783Srmacklem break; 463191783Srmacklem } 464191783Srmacklem return (dp); 465191783Srmacklem} 466191783Srmacklem 467191783Srmacklem/* 468191783Srmacklem * Get a stateid for an I/O operation. First, look for an open and iff 469191783Srmacklem * found, return either a lockowner stateid or the open stateid. 470191783Srmacklem * If no Open is found, just return error and the special stateid of all zeros. 471191783Srmacklem */ 472191783SrmacklemAPPLESTATIC int 473191783Srmacklemnfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode, 474244042Srmacklem int fords, struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp, 475191783Srmacklem void **lckpp) 476191783Srmacklem{ 477191783Srmacklem struct nfsclclient *clp; 478191783Srmacklem struct nfsclowner *owp; 479195510Srmacklem struct nfsclopen *op = NULL; 480191783Srmacklem struct nfscllockowner *lp; 481191783Srmacklem struct nfscldeleg *dp; 482191783Srmacklem struct nfsnode *np; 483191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 484191783Srmacklem int error, done; 485191783Srmacklem 486191783Srmacklem *lckpp = NULL; 487191783Srmacklem /* 488191783Srmacklem * Initially, just set the special stateid of all zeros. 489244042Srmacklem * (Don't do this for a DS, since the special stateid can't be used.) 490191783Srmacklem */ 491244042Srmacklem if (fords == 0) { 492244042Srmacklem stateidp->seqid = 0; 493244042Srmacklem stateidp->other[0] = 0; 494244042Srmacklem stateidp->other[1] = 0; 495244042Srmacklem stateidp->other[2] = 0; 496244042Srmacklem } 497191783Srmacklem if (vnode_vtype(vp) != VREG) 498191783Srmacklem return (EISDIR); 499191783Srmacklem np = VTONFS(vp); 500191783Srmacklem NFSLOCKCLSTATE(); 501191783Srmacklem clp = nfscl_findcl(VFSTONFS(vnode_mount(vp))); 502191783Srmacklem if (clp == NULL) { 503191783Srmacklem NFSUNLOCKCLSTATE(); 504191783Srmacklem return (EACCES); 505191783Srmacklem } 506191783Srmacklem 507191783Srmacklem /* 508206818Srmacklem * Wait for recovery to complete. 509206818Srmacklem */ 510206818Srmacklem while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG)) 511206818Srmacklem (void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR, 512206818Srmacklem PZERO, "nfsrecvr", NULL); 513206818Srmacklem 514206818Srmacklem /* 515191783Srmacklem * First, look for a delegation. 516191783Srmacklem */ 517191783Srmacklem LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) { 518191783Srmacklem if (dp->nfsdl_fhlen == fhlen && 519191783Srmacklem !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) { 520191783Srmacklem if (!(mode & NFSV4OPEN_ACCESSWRITE) || 521191783Srmacklem (dp->nfsdl_flags & NFSCLDL_WRITE)) { 522191783Srmacklem stateidp->seqid = dp->nfsdl_stateid.seqid; 523191783Srmacklem stateidp->other[0] = dp->nfsdl_stateid.other[0]; 524191783Srmacklem stateidp->other[1] = dp->nfsdl_stateid.other[1]; 525191783Srmacklem stateidp->other[2] = dp->nfsdl_stateid.other[2]; 526191783Srmacklem if (!(np->n_flag & NDELEGRECALL)) { 527191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, 528191783Srmacklem nfsdl_list); 529191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, 530191783Srmacklem nfsdl_list); 531191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 532191783Srmacklem 120; 533191783Srmacklem dp->nfsdl_rwlock.nfslock_usecnt++; 534191783Srmacklem *lckpp = (void *)&dp->nfsdl_rwlock; 535191783Srmacklem } 536191783Srmacklem NFSUNLOCKCLSTATE(); 537191783Srmacklem return (0); 538191783Srmacklem } 539191783Srmacklem break; 540191783Srmacklem } 541191783Srmacklem } 542191783Srmacklem 543191783Srmacklem if (p != NULL) { 544191783Srmacklem /* 545191783Srmacklem * If p != NULL, we want to search the parentage tree 546191783Srmacklem * for a matching OpenOwner and use that. 547191783Srmacklem */ 548222719Srmacklem nfscl_filllockowner(p->td_proc, own, F_POSIX); 549223774Srmacklem lp = NULL; 550223774Srmacklem error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, own, own, 551223774Srmacklem mode, &lp, &op); 552244042Srmacklem if (error == 0 && lp != NULL && fords == 0) { 553244042Srmacklem /* Don't return a lock stateid for a DS. */ 554223774Srmacklem stateidp->seqid = 555223774Srmacklem lp->nfsl_stateid.seqid; 556223774Srmacklem stateidp->other[0] = 557223774Srmacklem lp->nfsl_stateid.other[0]; 558223774Srmacklem stateidp->other[1] = 559223774Srmacklem lp->nfsl_stateid.other[1]; 560223774Srmacklem stateidp->other[2] = 561223774Srmacklem lp->nfsl_stateid.other[2]; 562223774Srmacklem NFSUNLOCKCLSTATE(); 563223774Srmacklem return (0); 564191783Srmacklem } 565195510Srmacklem } 566195510Srmacklem if (op == NULL) { 567195510Srmacklem /* If not found, just look for any OpenOwner that will work. */ 568191783Srmacklem done = 0; 569191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 570191783Srmacklem while (!done && owp != NULL) { 571195510Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 572195510Srmacklem if (op->nfso_fhlen == fhlen && 573195510Srmacklem !NFSBCMP(op->nfso_fh, nfhp, fhlen) && 574195510Srmacklem (mode & op->nfso_mode) == mode) { 575195510Srmacklem done = 1; 576195510Srmacklem break; 577195510Srmacklem } 578191783Srmacklem } 579195510Srmacklem if (!done) 580195510Srmacklem owp = LIST_NEXT(owp, nfsow_list); 581191783Srmacklem } 582191783Srmacklem if (!done) { 583191783Srmacklem NFSUNLOCKCLSTATE(); 584191783Srmacklem return (ENOENT); 585191783Srmacklem } 586231133Srmacklem /* 587231133Srmacklem * For read aheads or write behinds, use the open cred. 588231133Srmacklem * A read ahead or write behind is indicated by p == NULL. 589231133Srmacklem */ 590231133Srmacklem if (p == NULL) 591231133Srmacklem newnfs_copycred(&op->nfso_cred, cred); 592191783Srmacklem } 593191783Srmacklem 594191783Srmacklem /* 595191783Srmacklem * No lock stateid, so return the open stateid. 596191783Srmacklem */ 597191783Srmacklem stateidp->seqid = op->nfso_stateid.seqid; 598191783Srmacklem stateidp->other[0] = op->nfso_stateid.other[0]; 599191783Srmacklem stateidp->other[1] = op->nfso_stateid.other[1]; 600191783Srmacklem stateidp->other[2] = op->nfso_stateid.other[2]; 601191783Srmacklem NFSUNLOCKCLSTATE(); 602191783Srmacklem return (0); 603191783Srmacklem} 604191783Srmacklem 605191783Srmacklem/* 606223774Srmacklem * Search for a matching file, mode and, optionally, lockowner. 607191783Srmacklem */ 608191783Srmacklemstatic int 609191783Srmacklemnfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen, 610223774Srmacklem u_int8_t *openown, u_int8_t *lockown, u_int32_t mode, 611223774Srmacklem struct nfscllockowner **lpp, struct nfsclopen **opp) 612191783Srmacklem{ 613223774Srmacklem struct nfsclowner *owp; 614223774Srmacklem struct nfsclopen *op, *rop, *rop2; 615223774Srmacklem struct nfscllockowner *lp; 616223774Srmacklem int keep_looping; 617191783Srmacklem 618223774Srmacklem if (lpp != NULL) 619223774Srmacklem *lpp = NULL; 620223774Srmacklem /* 621223774Srmacklem * rop will be set to the open to be returned. There are three 622223774Srmacklem * variants of this, all for an open of the correct file: 623223774Srmacklem * 1 - A match of lockown. 624223774Srmacklem * 2 - A match of the openown, when no lockown match exists. 625223774Srmacklem * 3 - A match for any open, if no openown or lockown match exists. 626223774Srmacklem * Looking for #2 over #3 probably isn't necessary, but since 627223774Srmacklem * RFC3530 is vague w.r.t. the relationship between openowners and 628223774Srmacklem * lockowners, I think this is the safer way to go. 629223774Srmacklem */ 630223774Srmacklem rop = NULL; 631223774Srmacklem rop2 = NULL; 632223774Srmacklem keep_looping = 1; 633223774Srmacklem /* Search the client list */ 634223774Srmacklem owp = LIST_FIRST(ohp); 635223774Srmacklem while (owp != NULL && keep_looping != 0) { 636223774Srmacklem /* and look for the correct open */ 637223774Srmacklem op = LIST_FIRST(&owp->nfsow_open); 638223774Srmacklem while (op != NULL && keep_looping != 0) { 639223774Srmacklem if (op->nfso_fhlen == fhlen && 640223774Srmacklem !NFSBCMP(op->nfso_fh, nfhp, fhlen) 641223774Srmacklem && (op->nfso_mode & mode) == mode) { 642223774Srmacklem if (lpp != NULL) { 643223774Srmacklem /* Now look for a matching lockowner. */ 644223774Srmacklem LIST_FOREACH(lp, &op->nfso_lock, 645223774Srmacklem nfsl_list) { 646223774Srmacklem if (!NFSBCMP(lp->nfsl_owner, 647223774Srmacklem lockown, 648223774Srmacklem NFSV4CL_LOCKNAMELEN)) { 649223774Srmacklem *lpp = lp; 650223774Srmacklem rop = op; 651223774Srmacklem keep_looping = 0; 652223774Srmacklem break; 653223774Srmacklem } 654223774Srmacklem } 655191783Srmacklem } 656223774Srmacklem if (rop == NULL && !NFSBCMP(owp->nfsow_owner, 657223774Srmacklem openown, NFSV4CL_LOCKNAMELEN)) { 658223774Srmacklem rop = op; 659223774Srmacklem if (lpp == NULL) 660223774Srmacklem keep_looping = 0; 661223774Srmacklem } 662223774Srmacklem if (rop2 == NULL) 663223774Srmacklem rop2 = op; 664191783Srmacklem } 665223774Srmacklem op = LIST_NEXT(op, nfso_list); 666191783Srmacklem } 667223774Srmacklem owp = LIST_NEXT(owp, nfsow_list); 668191783Srmacklem } 669223774Srmacklem if (rop == NULL) 670223774Srmacklem rop = rop2; 671223774Srmacklem if (rop == NULL) 672191783Srmacklem return (EBADF); 673223774Srmacklem *opp = rop; 674191783Srmacklem return (0); 675191783Srmacklem} 676191783Srmacklem 677191783Srmacklem/* 678191783Srmacklem * Release use of an open owner. Called when open operations are done 679191783Srmacklem * with the open owner. 680191783Srmacklem */ 681191783SrmacklemAPPLESTATIC void 682191783Srmacklemnfscl_ownerrelease(struct nfsclowner *owp, __unused int error, 683191783Srmacklem __unused int candelete, int unlocked) 684191783Srmacklem{ 685191783Srmacklem 686191783Srmacklem if (owp == NULL) 687191783Srmacklem return; 688191783Srmacklem NFSLOCKCLSTATE(); 689191783Srmacklem if (!unlocked) 690191783Srmacklem nfscl_lockunlock(&owp->nfsow_rwlock); 691191783Srmacklem nfscl_clrelease(owp->nfsow_clp); 692191783Srmacklem NFSUNLOCKCLSTATE(); 693191783Srmacklem} 694191783Srmacklem 695191783Srmacklem/* 696191783Srmacklem * Release use of an open structure under an open owner. 697191783Srmacklem */ 698191783SrmacklemAPPLESTATIC void 699191783Srmacklemnfscl_openrelease(struct nfsclopen *op, int error, int candelete) 700191783Srmacklem{ 701191783Srmacklem struct nfsclclient *clp; 702191783Srmacklem struct nfsclowner *owp; 703191783Srmacklem 704191783Srmacklem if (op == NULL) 705191783Srmacklem return; 706191783Srmacklem NFSLOCKCLSTATE(); 707191783Srmacklem owp = op->nfso_own; 708191783Srmacklem nfscl_lockunlock(&owp->nfsow_rwlock); 709191783Srmacklem clp = owp->nfsow_clp; 710191783Srmacklem if (error && candelete && op->nfso_opencnt == 0) 711191783Srmacklem nfscl_freeopen(op, 0); 712191783Srmacklem nfscl_clrelease(clp); 713191783Srmacklem NFSUNLOCKCLSTATE(); 714191783Srmacklem} 715191783Srmacklem 716191783Srmacklem/* 717191783Srmacklem * Called to get a clientid structure. It will optionally lock the 718191783Srmacklem * client data structures to do the SetClientId/SetClientId_confirm, 719191783Srmacklem * but will release that lock and return the clientid with a refernce 720191783Srmacklem * count on it. 721193735Srmacklem * If the "cred" argument is NULL, a new clientid should not be created. 722193735Srmacklem * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot 723193735Srmacklem * be done. 724244042Srmacklem * The start_renewthread argument tells nfscl_getcl() to start a renew 725244042Srmacklem * thread if this creates a new clp. 726192337Srmacklem * It always clpp with a reference count on it, unless returning an error. 727191783Srmacklem */ 728191783SrmacklemAPPLESTATIC int 729244042Srmacklemnfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p, 730244042Srmacklem int start_renewthread, struct nfsclclient **clpp) 731191783Srmacklem{ 732191783Srmacklem struct nfsclclient *clp; 733193735Srmacklem struct nfsclclient *newclp = NULL; 734222389Srmacklem struct nfsmount *nmp; 735193066Sjamie char uuid[HOSTUUIDLEN]; 736191783Srmacklem int igotlock = 0, error, trystalecnt, clidinusedelay, i; 737193735Srmacklem u_int16_t idlen = 0; 738191783Srmacklem 739222389Srmacklem nmp = VFSTONFS(mp); 740193735Srmacklem if (cred != NULL) { 741194117Sjamie getcredhostuuid(cred, uuid, sizeof uuid); 742193735Srmacklem idlen = strlen(uuid); 743193735Srmacklem if (idlen > 0) 744193735Srmacklem idlen += sizeof (u_int64_t); 745193735Srmacklem else 746193735Srmacklem idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */ 747193735Srmacklem MALLOC(newclp, struct nfsclclient *, 748193735Srmacklem sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT, 749244042Srmacklem M_WAITOK | M_ZERO); 750193735Srmacklem } 751191783Srmacklem NFSLOCKCLSTATE(); 752222389Srmacklem /* 753222389Srmacklem * If a forced dismount is already in progress, don't 754222389Srmacklem * allocate a new clientid and get out now. For the case where 755222389Srmacklem * clp != NULL, this is a harmless optimization. 756222389Srmacklem */ 757222389Srmacklem if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 758222389Srmacklem NFSUNLOCKCLSTATE(); 759222389Srmacklem if (newclp != NULL) 760222389Srmacklem free(newclp, M_NFSCLCLIENT); 761222389Srmacklem return (EBADF); 762222389Srmacklem } 763191783Srmacklem clp = nmp->nm_clp; 764191783Srmacklem if (clp == NULL) { 765193735Srmacklem if (newclp == NULL) { 766193735Srmacklem NFSUNLOCKCLSTATE(); 767193735Srmacklem return (EACCES); 768193735Srmacklem } 769191783Srmacklem clp = newclp; 770191783Srmacklem clp->nfsc_idlen = idlen; 771191783Srmacklem LIST_INIT(&clp->nfsc_owner); 772191783Srmacklem TAILQ_INIT(&clp->nfsc_deleg); 773244042Srmacklem TAILQ_INIT(&clp->nfsc_layout); 774244042Srmacklem LIST_INIT(&clp->nfsc_devinfo); 775191783Srmacklem for (i = 0; i < NFSCLDELEGHASHSIZE; i++) 776191783Srmacklem LIST_INIT(&clp->nfsc_deleghash[i]); 777244042Srmacklem for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++) 778244042Srmacklem LIST_INIT(&clp->nfsc_layouthash[i]); 779191783Srmacklem clp->nfsc_flags = NFSCLFLAGS_INITED; 780191783Srmacklem clp->nfsc_clientidrev = 1; 781191783Srmacklem clp->nfsc_cbident = nfscl_nextcbident(); 782193066Sjamie nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id, 783191783Srmacklem clp->nfsc_idlen); 784191783Srmacklem LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list); 785191783Srmacklem nmp->nm_clp = clp; 786191783Srmacklem clp->nfsc_nmp = nmp; 787191783Srmacklem NFSUNLOCKCLSTATE(); 788244042Srmacklem if (start_renewthread != 0) 789244042Srmacklem nfscl_start_renewthread(clp); 790191783Srmacklem } else { 791191783Srmacklem NFSUNLOCKCLSTATE(); 792193735Srmacklem if (newclp != NULL) 793244042Srmacklem free(newclp, M_NFSCLCLIENT); 794191783Srmacklem } 795191783Srmacklem NFSLOCKCLSTATE(); 796223971Srmacklem while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock && 797223971Srmacklem (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0) 798191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 799222389Srmacklem NFSCLSTATEMUTEXPTR, mp); 800191783Srmacklem if (!igotlock) 801222389Srmacklem nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp); 802222389Srmacklem if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 803222389Srmacklem /* 804222389Srmacklem * Both nfsv4_lock() and nfsv4_getref() know to check 805222389Srmacklem * for MNTK_UNMOUNTF and return without sleeping to 806222389Srmacklem * wait for the exclusive lock to be released, since it 807222389Srmacklem * might be held by nfscl_umount() and we need to get out 808222389Srmacklem * now for that case and not wait until nfscl_umount() 809222389Srmacklem * releases it. 810222389Srmacklem */ 811222389Srmacklem NFSUNLOCKCLSTATE(); 812222389Srmacklem return (EBADF); 813222389Srmacklem } 814191783Srmacklem NFSUNLOCKCLSTATE(); 815191783Srmacklem 816191783Srmacklem /* 817191783Srmacklem * If it needs a clientid, do the setclientid now. 818191783Srmacklem */ 819191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) { 820191783Srmacklem if (!igotlock) 821191783Srmacklem panic("nfscl_clget"); 822193735Srmacklem if (p == NULL || cred == NULL) { 823191783Srmacklem NFSLOCKCLSTATE(); 824191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 825191783Srmacklem NFSUNLOCKCLSTATE(); 826191783Srmacklem return (EACCES); 827191783Srmacklem } 828191783Srmacklem /* 829191783Srmacklem * If RFC3530 Sec. 14.2.33 is taken literally, 830191783Srmacklem * NFSERR_CLIDINUSE will be returned persistently for the 831191783Srmacklem * case where a new mount of the same file system is using 832191783Srmacklem * a different principal. In practice, NFSERR_CLIDINUSE is 833191783Srmacklem * only returned when there is outstanding unexpired state 834191783Srmacklem * on the clientid. As such, try for twice the lease 835191783Srmacklem * interval, if we know what that is. Otherwise, make a 836191783Srmacklem * wild ass guess. 837191783Srmacklem * The case of returning NFSERR_STALECLIENTID is far less 838191783Srmacklem * likely, but might occur if there is a significant delay 839191783Srmacklem * between doing the SetClientID and SetClientIDConfirm Ops, 840191783Srmacklem * such that the server throws away the clientid before 841191783Srmacklem * receiving the SetClientIDConfirm. 842191783Srmacklem */ 843191783Srmacklem if (clp->nfsc_renew > 0) 844191783Srmacklem clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2; 845191783Srmacklem else 846191783Srmacklem clidinusedelay = 120; 847191783Srmacklem trystalecnt = 3; 848191783Srmacklem do { 849244042Srmacklem error = nfsrpc_setclient(nmp, clp, 0, cred, p); 850191783Srmacklem if (error == NFSERR_STALECLIENTID || 851191783Srmacklem error == NFSERR_STALEDONTRECOVER || 852244042Srmacklem error == NFSERR_BADSESSION || 853191783Srmacklem error == NFSERR_CLIDINUSE) { 854207170Srmacklem (void) nfs_catnap(PZERO, error, "nfs_setcl"); 855191783Srmacklem } 856191783Srmacklem } while (((error == NFSERR_STALECLIENTID || 857244042Srmacklem error == NFSERR_BADSESSION || 858191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) || 859191783Srmacklem (error == NFSERR_CLIDINUSE && --clidinusedelay > 0)); 860191783Srmacklem if (error) { 861191783Srmacklem NFSLOCKCLSTATE(); 862191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 863191783Srmacklem NFSUNLOCKCLSTATE(); 864191783Srmacklem return (error); 865191783Srmacklem } 866191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 867191783Srmacklem } 868191783Srmacklem if (igotlock) { 869191783Srmacklem NFSLOCKCLSTATE(); 870191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 1); 871191783Srmacklem NFSUNLOCKCLSTATE(); 872191783Srmacklem } 873191783Srmacklem 874191783Srmacklem *clpp = clp; 875191783Srmacklem return (0); 876191783Srmacklem} 877191783Srmacklem 878191783Srmacklem/* 879191783Srmacklem * Get a reference to a clientid and return it, if valid. 880191783Srmacklem */ 881191783SrmacklemAPPLESTATIC struct nfsclclient * 882191783Srmacklemnfscl_findcl(struct nfsmount *nmp) 883191783Srmacklem{ 884191783Srmacklem struct nfsclclient *clp; 885191783Srmacklem 886191783Srmacklem clp = nmp->nm_clp; 887191783Srmacklem if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) 888191783Srmacklem return (NULL); 889191783Srmacklem return (clp); 890191783Srmacklem} 891191783Srmacklem 892191783Srmacklem/* 893191783Srmacklem * Release the clientid structure. It may be locked or reference counted. 894191783Srmacklem */ 895191783Srmacklemstatic void 896191783Srmacklemnfscl_clrelease(struct nfsclclient *clp) 897191783Srmacklem{ 898191783Srmacklem 899191783Srmacklem if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK) 900191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 901191783Srmacklem else 902191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 903191783Srmacklem} 904191783Srmacklem 905191783Srmacklem/* 906191783Srmacklem * External call for nfscl_clrelease. 907191783Srmacklem */ 908191783SrmacklemAPPLESTATIC void 909191783Srmacklemnfscl_clientrelease(struct nfsclclient *clp) 910191783Srmacklem{ 911191783Srmacklem 912191783Srmacklem NFSLOCKCLSTATE(); 913191783Srmacklem if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK) 914191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 915191783Srmacklem else 916191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 917191783Srmacklem NFSUNLOCKCLSTATE(); 918191783Srmacklem} 919191783Srmacklem 920191783Srmacklem/* 921191783Srmacklem * Called when wanting to lock a byte region. 922191783Srmacklem */ 923191783SrmacklemAPPLESTATIC int 924191783Srmacklemnfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len, 925191783Srmacklem short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp, 926222719Srmacklem int recovery, void *id, int flags, u_int8_t *rownp, u_int8_t *ropenownp, 927191783Srmacklem struct nfscllockowner **lpp, int *newonep, int *donelocallyp) 928191783Srmacklem{ 929191783Srmacklem struct nfscllockowner *lp; 930191783Srmacklem struct nfsclopen *op; 931191783Srmacklem struct nfsclclient *clp; 932191783Srmacklem struct nfscllockowner *nlp; 933191783Srmacklem struct nfscllock *nlop, *otherlop; 934191783Srmacklem struct nfscldeleg *dp = NULL, *ldp = NULL; 935191783Srmacklem struct nfscllockownerhead *lhp = NULL; 936191783Srmacklem struct nfsnode *np; 937223774Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp, openown[NFSV4CL_LOCKNAMELEN]; 938223774Srmacklem u_int8_t *openownp; 939191783Srmacklem int error = 0, ret, donelocally = 0; 940191783Srmacklem u_int32_t mode; 941191783Srmacklem 942223774Srmacklem /* For Lock Ops, the open mode doesn't matter, so use 0 to match any. */ 943223774Srmacklem mode = 0; 944191783Srmacklem np = VTONFS(vp); 945191783Srmacklem *lpp = NULL; 946223774Srmacklem lp = NULL; 947191783Srmacklem *newonep = 0; 948191783Srmacklem *donelocallyp = 0; 949191783Srmacklem 950191783Srmacklem /* 951191783Srmacklem * Might need these, so MALLOC them now, to 952191783Srmacklem * avoid a tsleep() in MALLOC later. 953191783Srmacklem */ 954191783Srmacklem MALLOC(nlp, struct nfscllockowner *, 955191783Srmacklem sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK); 956191783Srmacklem MALLOC(otherlop, struct nfscllock *, 957191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 958191783Srmacklem MALLOC(nlop, struct nfscllock *, 959191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 960191783Srmacklem nlop->nfslo_type = type; 961191783Srmacklem nlop->nfslo_first = off; 962191783Srmacklem if (len == NFS64BITSSET) { 963191783Srmacklem nlop->nfslo_end = NFS64BITSSET; 964191783Srmacklem } else { 965191783Srmacklem nlop->nfslo_end = off + len; 966191783Srmacklem if (nlop->nfslo_end <= nlop->nfslo_first) 967191783Srmacklem error = NFSERR_INVAL; 968191783Srmacklem } 969191783Srmacklem 970191783Srmacklem if (!error) { 971191783Srmacklem if (recovery) 972191783Srmacklem clp = rclp; 973191783Srmacklem else 974244042Srmacklem error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp); 975191783Srmacklem } 976191783Srmacklem if (error) { 977191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 978191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 979191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 980191783Srmacklem return (error); 981191783Srmacklem } 982191783Srmacklem 983191783Srmacklem op = NULL; 984191783Srmacklem if (recovery) { 985191783Srmacklem ownp = rownp; 986223774Srmacklem openownp = ropenownp; 987191783Srmacklem } else { 988222719Srmacklem nfscl_filllockowner(id, own, flags); 989191783Srmacklem ownp = own; 990223774Srmacklem nfscl_filllockowner(p->td_proc, openown, F_POSIX); 991223774Srmacklem openownp = openown; 992191783Srmacklem } 993191783Srmacklem if (!recovery) { 994191783Srmacklem NFSLOCKCLSTATE(); 995191783Srmacklem /* 996191783Srmacklem * First, search for a delegation. If one exists for this file, 997191783Srmacklem * the lock can be done locally against it, so long as there 998191783Srmacklem * isn't a local lock conflict. 999191783Srmacklem */ 1000191783Srmacklem ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 1001191783Srmacklem np->n_fhp->nfh_len); 1002191783Srmacklem /* Just sanity check for correct type of delegation */ 1003214406Srmacklem if (dp != NULL && ((dp->nfsdl_flags & 1004214406Srmacklem (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 || 1005214406Srmacklem (type == F_WRLCK && 1006214406Srmacklem (dp->nfsdl_flags & NFSCLDL_WRITE) == 0))) 1007191783Srmacklem dp = NULL; 1008191783Srmacklem } 1009191783Srmacklem if (dp != NULL) { 1010223774Srmacklem /* Now, find an open and maybe a lockowner. */ 1011191783Srmacklem ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh, 1012223774Srmacklem np->n_fhp->nfh_len, openownp, ownp, mode, NULL, &op); 1013191783Srmacklem if (ret) 1014191783Srmacklem ret = nfscl_getopen(&clp->nfsc_owner, 1015223774Srmacklem np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp, 1016223774Srmacklem ownp, mode, NULL, &op); 1017191783Srmacklem if (!ret) { 1018191783Srmacklem lhp = &dp->nfsdl_lock; 1019191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 1020191783Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list); 1021191783Srmacklem dp->nfsdl_timestamp = NFSD_MONOSEC + 120; 1022191783Srmacklem donelocally = 1; 1023191783Srmacklem } else { 1024191783Srmacklem dp = NULL; 1025191783Srmacklem } 1026191783Srmacklem } 1027191783Srmacklem if (!donelocally) { 1028191783Srmacklem /* 1029223774Srmacklem * Get the related Open and maybe lockowner. 1030191783Srmacklem */ 1031223774Srmacklem error = nfscl_getopen(&clp->nfsc_owner, 1032223774Srmacklem np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp, 1033223774Srmacklem ownp, mode, &lp, &op); 1034191783Srmacklem if (!error) 1035191783Srmacklem lhp = &op->nfso_lock; 1036191783Srmacklem } 1037191783Srmacklem if (!error && !recovery) 1038201439Srmacklem error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, 1039201439Srmacklem np->n_fhp->nfh_len, nlop, ownp, ldp, NULL); 1040191783Srmacklem if (error) { 1041191783Srmacklem if (!recovery) { 1042191783Srmacklem nfscl_clrelease(clp); 1043191783Srmacklem NFSUNLOCKCLSTATE(); 1044191783Srmacklem } 1045191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 1046191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 1047191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1048191783Srmacklem return (error); 1049191783Srmacklem } 1050191783Srmacklem 1051191783Srmacklem /* 1052191783Srmacklem * Ok, see if a lockowner exists and create one, as required. 1053191783Srmacklem */ 1054223774Srmacklem if (lp == NULL) 1055223774Srmacklem LIST_FOREACH(lp, lhp, nfsl_list) { 1056223774Srmacklem if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN)) 1057223774Srmacklem break; 1058223774Srmacklem } 1059191783Srmacklem if (lp == NULL) { 1060191783Srmacklem NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN); 1061191783Srmacklem if (recovery) 1062191783Srmacklem NFSBCOPY(ropenownp, nlp->nfsl_openowner, 1063191783Srmacklem NFSV4CL_LOCKNAMELEN); 1064191783Srmacklem else 1065191783Srmacklem NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner, 1066191783Srmacklem NFSV4CL_LOCKNAMELEN); 1067191783Srmacklem nlp->nfsl_seqid = 0; 1068228217Srmacklem nlp->nfsl_lockflags = flags; 1069191783Srmacklem nlp->nfsl_inprog = NULL; 1070191783Srmacklem nfscl_lockinit(&nlp->nfsl_rwlock); 1071191783Srmacklem LIST_INIT(&nlp->nfsl_lock); 1072191783Srmacklem if (donelocally) { 1073191783Srmacklem nlp->nfsl_open = NULL; 1074191783Srmacklem newnfsstats.cllocallockowners++; 1075191783Srmacklem } else { 1076191783Srmacklem nlp->nfsl_open = op; 1077191783Srmacklem newnfsstats.cllockowners++; 1078191783Srmacklem } 1079191783Srmacklem LIST_INSERT_HEAD(lhp, nlp, nfsl_list); 1080191783Srmacklem lp = nlp; 1081191783Srmacklem nlp = NULL; 1082191783Srmacklem *newonep = 1; 1083191783Srmacklem } 1084191783Srmacklem 1085191783Srmacklem /* 1086191783Srmacklem * Now, update the byte ranges for locks. 1087191783Srmacklem */ 1088191783Srmacklem ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally); 1089191783Srmacklem if (!ret) 1090191783Srmacklem donelocally = 1; 1091191783Srmacklem if (donelocally) { 1092191783Srmacklem *donelocallyp = 1; 1093191783Srmacklem if (!recovery) 1094191783Srmacklem nfscl_clrelease(clp); 1095191783Srmacklem } else { 1096191783Srmacklem /* 1097191783Srmacklem * Serial modifications on the lock owner for multiple threads 1098191783Srmacklem * for the same process using a read/write lock. 1099191783Srmacklem */ 1100191783Srmacklem if (!recovery) 1101191783Srmacklem nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR); 1102191783Srmacklem } 1103191783Srmacklem if (!recovery) 1104191783Srmacklem NFSUNLOCKCLSTATE(); 1105191783Srmacklem 1106191783Srmacklem if (nlp) 1107191783Srmacklem FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); 1108191783Srmacklem if (nlop) 1109191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1110191783Srmacklem if (otherlop) 1111191783Srmacklem FREE((caddr_t)otherlop, M_NFSCLLOCK); 1112191783Srmacklem 1113191783Srmacklem *lpp = lp; 1114191783Srmacklem return (0); 1115191783Srmacklem} 1116191783Srmacklem 1117191783Srmacklem/* 1118191783Srmacklem * Called to unlock a byte range, for LockU. 1119191783Srmacklem */ 1120191783SrmacklemAPPLESTATIC int 1121191783Srmacklemnfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len, 1122191783Srmacklem __unused struct ucred *cred, NFSPROC_T *p, int callcnt, 1123222719Srmacklem struct nfsclclient *clp, void *id, int flags, 1124222719Srmacklem struct nfscllockowner **lpp, int *dorpcp) 1125191783Srmacklem{ 1126191783Srmacklem struct nfscllockowner *lp; 1127191783Srmacklem struct nfsclowner *owp; 1128191783Srmacklem struct nfsclopen *op; 1129191783Srmacklem struct nfscllock *nlop, *other_lop = NULL; 1130191783Srmacklem struct nfscldeleg *dp; 1131191783Srmacklem struct nfsnode *np; 1132191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1133201439Srmacklem int ret = 0, fnd; 1134191783Srmacklem 1135191783Srmacklem np = VTONFS(vp); 1136191783Srmacklem *lpp = NULL; 1137191783Srmacklem *dorpcp = 0; 1138191783Srmacklem 1139191783Srmacklem /* 1140191783Srmacklem * Might need these, so MALLOC them now, to 1141191783Srmacklem * avoid a tsleep() in MALLOC later. 1142191783Srmacklem */ 1143191783Srmacklem MALLOC(nlop, struct nfscllock *, 1144191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 1145191783Srmacklem nlop->nfslo_type = F_UNLCK; 1146191783Srmacklem nlop->nfslo_first = off; 1147191783Srmacklem if (len == NFS64BITSSET) { 1148191783Srmacklem nlop->nfslo_end = NFS64BITSSET; 1149191783Srmacklem } else { 1150191783Srmacklem nlop->nfslo_end = off + len; 1151191783Srmacklem if (nlop->nfslo_end <= nlop->nfslo_first) { 1152191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1153191783Srmacklem return (NFSERR_INVAL); 1154191783Srmacklem } 1155191783Srmacklem } 1156191783Srmacklem if (callcnt == 0) { 1157191783Srmacklem MALLOC(other_lop, struct nfscllock *, 1158191783Srmacklem sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK); 1159191783Srmacklem *other_lop = *nlop; 1160191783Srmacklem } 1161222719Srmacklem nfscl_filllockowner(id, own, flags); 1162191783Srmacklem dp = NULL; 1163191783Srmacklem NFSLOCKCLSTATE(); 1164191783Srmacklem if (callcnt == 0) 1165191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 1166191783Srmacklem np->n_fhp->nfh_len); 1167191783Srmacklem 1168191783Srmacklem /* 1169191783Srmacklem * First, unlock any local regions on a delegation. 1170191783Srmacklem */ 1171191783Srmacklem if (dp != NULL) { 1172191783Srmacklem /* Look for this lockowner. */ 1173191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 1174191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1175191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1176191783Srmacklem break; 1177191783Srmacklem } 1178191783Srmacklem if (lp != NULL) 1179191783Srmacklem /* Use other_lop, so nlop is still available */ 1180191783Srmacklem (void)nfscl_updatelock(lp, &other_lop, NULL, 1); 1181191783Srmacklem } 1182191783Srmacklem 1183191783Srmacklem /* 1184191783Srmacklem * Now, find a matching open/lockowner that hasn't already been done, 1185191783Srmacklem * as marked by nfsl_inprog. 1186191783Srmacklem */ 1187191783Srmacklem lp = NULL; 1188191783Srmacklem fnd = 0; 1189191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1190191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1191191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1192191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1193191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1194191783Srmacklem if (lp->nfsl_inprog == NULL && 1195191783Srmacklem !NFSBCMP(lp->nfsl_owner, own, 1196191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1197191783Srmacklem fnd = 1; 1198191783Srmacklem break; 1199191783Srmacklem } 1200191783Srmacklem } 1201191783Srmacklem if (fnd) 1202191783Srmacklem break; 1203191783Srmacklem } 1204191783Srmacklem } 1205191783Srmacklem if (fnd) 1206191783Srmacklem break; 1207191783Srmacklem } 1208191783Srmacklem 1209191783Srmacklem if (lp != NULL) { 1210191783Srmacklem ret = nfscl_updatelock(lp, &nlop, NULL, 0); 1211191783Srmacklem if (ret) 1212191783Srmacklem *dorpcp = 1; 1213191783Srmacklem /* 1214191783Srmacklem * Serial modifications on the lock owner for multiple 1215191783Srmacklem * threads for the same process using a read/write lock. 1216191783Srmacklem */ 1217191783Srmacklem lp->nfsl_inprog = p; 1218191783Srmacklem nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR); 1219191783Srmacklem *lpp = lp; 1220191783Srmacklem } 1221191783Srmacklem NFSUNLOCKCLSTATE(); 1222191783Srmacklem if (nlop) 1223191783Srmacklem FREE((caddr_t)nlop, M_NFSCLLOCK); 1224191783Srmacklem if (other_lop) 1225191783Srmacklem FREE((caddr_t)other_lop, M_NFSCLLOCK); 1226191783Srmacklem return (0); 1227191783Srmacklem} 1228191783Srmacklem 1229191783Srmacklem/* 1230191783Srmacklem * Release all lockowners marked in progess for this process and file. 1231191783Srmacklem */ 1232191783SrmacklemAPPLESTATIC void 1233222719Srmacklemnfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p, 1234222719Srmacklem void *id, int flags) 1235191783Srmacklem{ 1236191783Srmacklem struct nfsclowner *owp; 1237191783Srmacklem struct nfsclopen *op; 1238191783Srmacklem struct nfscllockowner *lp; 1239191783Srmacklem struct nfsnode *np; 1240191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1241191783Srmacklem 1242191783Srmacklem np = VTONFS(vp); 1243222719Srmacklem nfscl_filllockowner(id, own, flags); 1244191783Srmacklem NFSLOCKCLSTATE(); 1245191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1246191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1247191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1248191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1249191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1250191783Srmacklem if (lp->nfsl_inprog == p && 1251191783Srmacklem !NFSBCMP(lp->nfsl_owner, own, 1252191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1253191783Srmacklem lp->nfsl_inprog = NULL; 1254191783Srmacklem nfscl_lockunlock(&lp->nfsl_rwlock); 1255191783Srmacklem } 1256191783Srmacklem } 1257191783Srmacklem } 1258191783Srmacklem } 1259191783Srmacklem } 1260191783Srmacklem nfscl_clrelease(clp); 1261191783Srmacklem NFSUNLOCKCLSTATE(); 1262191783Srmacklem} 1263191783Srmacklem 1264191783Srmacklem/* 1265191783Srmacklem * Called to find out if any bytes within the byte range specified are 1266191783Srmacklem * write locked by the calling process. Used to determine if flushing 1267191783Srmacklem * is required before a LockU. 1268191783Srmacklem * If in doubt, return 1, so the flush will occur. 1269191783Srmacklem */ 1270191783SrmacklemAPPLESTATIC int 1271191783Srmacklemnfscl_checkwritelocked(vnode_t vp, struct flock *fl, 1272222719Srmacklem struct ucred *cred, NFSPROC_T *p, void *id, int flags) 1273191783Srmacklem{ 1274191783Srmacklem struct nfsclowner *owp; 1275191783Srmacklem struct nfscllockowner *lp; 1276191783Srmacklem struct nfsclopen *op; 1277191783Srmacklem struct nfsclclient *clp; 1278191783Srmacklem struct nfscllock *lop; 1279191783Srmacklem struct nfscldeleg *dp; 1280191783Srmacklem struct nfsnode *np; 1281191783Srmacklem u_int64_t off, end; 1282191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 1283191783Srmacklem int error = 0; 1284191783Srmacklem 1285191783Srmacklem np = VTONFS(vp); 1286191783Srmacklem switch (fl->l_whence) { 1287191783Srmacklem case SEEK_SET: 1288191783Srmacklem case SEEK_CUR: 1289191783Srmacklem /* 1290191783Srmacklem * Caller is responsible for adding any necessary offset 1291191783Srmacklem * when SEEK_CUR is used. 1292191783Srmacklem */ 1293191783Srmacklem off = fl->l_start; 1294191783Srmacklem break; 1295191783Srmacklem case SEEK_END: 1296191783Srmacklem off = np->n_size + fl->l_start; 1297191783Srmacklem break; 1298191783Srmacklem default: 1299191783Srmacklem return (1); 1300191783Srmacklem }; 1301191783Srmacklem if (fl->l_len != 0) { 1302191783Srmacklem end = off + fl->l_len; 1303191783Srmacklem if (end < off) 1304191783Srmacklem return (1); 1305191783Srmacklem } else { 1306191783Srmacklem end = NFS64BITSSET; 1307191783Srmacklem } 1308191783Srmacklem 1309244042Srmacklem error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp); 1310191783Srmacklem if (error) 1311191783Srmacklem return (1); 1312222719Srmacklem nfscl_filllockowner(id, own, flags); 1313191783Srmacklem NFSLOCKCLSTATE(); 1314191783Srmacklem 1315191783Srmacklem /* 1316191783Srmacklem * First check the delegation locks. 1317191783Srmacklem */ 1318191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 1319191783Srmacklem if (dp != NULL) { 1320191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 1321191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1322191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1323191783Srmacklem break; 1324191783Srmacklem } 1325191783Srmacklem if (lp != NULL) { 1326191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 1327191783Srmacklem if (lop->nfslo_first >= end) 1328191783Srmacklem break; 1329191783Srmacklem if (lop->nfslo_end <= off) 1330191783Srmacklem continue; 1331191783Srmacklem if (lop->nfslo_type == F_WRLCK) { 1332191783Srmacklem nfscl_clrelease(clp); 1333191783Srmacklem NFSUNLOCKCLSTATE(); 1334191783Srmacklem return (1); 1335191783Srmacklem } 1336191783Srmacklem } 1337191783Srmacklem } 1338191783Srmacklem } 1339191783Srmacklem 1340191783Srmacklem /* 1341191783Srmacklem * Now, check state against the server. 1342191783Srmacklem */ 1343191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 1344191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1345191783Srmacklem if (op->nfso_fhlen == np->n_fhp->nfh_len && 1346191783Srmacklem !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) { 1347191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1348191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, 1349191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1350191783Srmacklem break; 1351191783Srmacklem } 1352191783Srmacklem if (lp != NULL) { 1353191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 1354191783Srmacklem if (lop->nfslo_first >= end) 1355191783Srmacklem break; 1356191783Srmacklem if (lop->nfslo_end <= off) 1357191783Srmacklem continue; 1358191783Srmacklem if (lop->nfslo_type == F_WRLCK) { 1359191783Srmacklem nfscl_clrelease(clp); 1360191783Srmacklem NFSUNLOCKCLSTATE(); 1361191783Srmacklem return (1); 1362191783Srmacklem } 1363191783Srmacklem } 1364191783Srmacklem } 1365191783Srmacklem } 1366191783Srmacklem } 1367191783Srmacklem } 1368191783Srmacklem nfscl_clrelease(clp); 1369191783Srmacklem NFSUNLOCKCLSTATE(); 1370191783Srmacklem return (0); 1371191783Srmacklem} 1372191783Srmacklem 1373191783Srmacklem/* 1374191783Srmacklem * Release a byte range lock owner structure. 1375191783Srmacklem */ 1376191783SrmacklemAPPLESTATIC void 1377191783Srmacklemnfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete) 1378191783Srmacklem{ 1379191783Srmacklem struct nfsclclient *clp; 1380191783Srmacklem 1381191783Srmacklem if (lp == NULL) 1382191783Srmacklem return; 1383191783Srmacklem NFSLOCKCLSTATE(); 1384191783Srmacklem clp = lp->nfsl_open->nfso_own->nfsow_clp; 1385191783Srmacklem if (error != 0 && candelete && 1386191783Srmacklem (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0) 1387191783Srmacklem nfscl_freelockowner(lp, 0); 1388191783Srmacklem else 1389191783Srmacklem nfscl_lockunlock(&lp->nfsl_rwlock); 1390191783Srmacklem nfscl_clrelease(clp); 1391191783Srmacklem NFSUNLOCKCLSTATE(); 1392191783Srmacklem} 1393191783Srmacklem 1394191783Srmacklem/* 1395191783Srmacklem * Free up an open structure and any associated byte range lock structures. 1396191783Srmacklem */ 1397191783SrmacklemAPPLESTATIC void 1398191783Srmacklemnfscl_freeopen(struct nfsclopen *op, int local) 1399191783Srmacklem{ 1400191783Srmacklem 1401191783Srmacklem LIST_REMOVE(op, nfso_list); 1402191783Srmacklem nfscl_freealllocks(&op->nfso_lock, local); 1403191783Srmacklem FREE((caddr_t)op, M_NFSCLOPEN); 1404191783Srmacklem if (local) 1405191783Srmacklem newnfsstats.cllocalopens--; 1406191783Srmacklem else 1407191783Srmacklem newnfsstats.clopens--; 1408191783Srmacklem} 1409191783Srmacklem 1410191783Srmacklem/* 1411191783Srmacklem * Free up all lock owners and associated locks. 1412191783Srmacklem */ 1413191783Srmacklemstatic void 1414191783Srmacklemnfscl_freealllocks(struct nfscllockownerhead *lhp, int local) 1415191783Srmacklem{ 1416191783Srmacklem struct nfscllockowner *lp, *nlp; 1417191783Srmacklem 1418191783Srmacklem LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) { 1419191783Srmacklem if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED)) 1420191783Srmacklem panic("nfscllckw"); 1421191783Srmacklem nfscl_freelockowner(lp, local); 1422191783Srmacklem } 1423191783Srmacklem} 1424191783Srmacklem 1425191783Srmacklem/* 1426191783Srmacklem * Called for an Open when NFSERR_EXPIRED is received from the server. 1427191783Srmacklem * If there are no byte range locks nor a Share Deny lost, try to do a 1428191783Srmacklem * fresh Open. Otherwise, free the open. 1429191783Srmacklem */ 1430191783Srmacklemstatic int 1431191783Srmacklemnfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op, 1432191783Srmacklem struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p) 1433191783Srmacklem{ 1434191783Srmacklem struct nfscllockowner *lp; 1435191783Srmacklem struct nfscldeleg *dp; 1436191783Srmacklem int mustdelete = 0, error; 1437191783Srmacklem 1438191783Srmacklem /* 1439191783Srmacklem * Look for any byte range lock(s). 1440191783Srmacklem */ 1441191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 1442191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 1443191783Srmacklem mustdelete = 1; 1444191783Srmacklem break; 1445191783Srmacklem } 1446191783Srmacklem } 1447191783Srmacklem 1448191783Srmacklem /* 1449191783Srmacklem * If no byte range lock(s) nor a Share deny, try to re-open. 1450191783Srmacklem */ 1451191783Srmacklem if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) { 1452191783Srmacklem newnfs_copycred(&op->nfso_cred, cred); 1453191783Srmacklem dp = NULL; 1454191783Srmacklem error = nfsrpc_reopen(nmp, op->nfso_fh, 1455191783Srmacklem op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p); 1456191783Srmacklem if (error) { 1457191783Srmacklem mustdelete = 1; 1458191783Srmacklem if (dp != NULL) { 1459191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 1460191783Srmacklem dp = NULL; 1461191783Srmacklem } 1462191783Srmacklem } 1463191783Srmacklem if (dp != NULL) 1464191783Srmacklem nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh, 1465191783Srmacklem op->nfso_fhlen, cred, p, &dp); 1466191783Srmacklem } 1467191783Srmacklem 1468191783Srmacklem /* 1469191783Srmacklem * If a byte range lock or Share deny or couldn't re-open, free it. 1470191783Srmacklem */ 1471191783Srmacklem if (mustdelete) 1472191783Srmacklem nfscl_freeopen(op, 0); 1473191783Srmacklem return (mustdelete); 1474191783Srmacklem} 1475191783Srmacklem 1476191783Srmacklem/* 1477191783Srmacklem * Free up an open owner structure. 1478191783Srmacklem */ 1479191783Srmacklemstatic void 1480191783Srmacklemnfscl_freeopenowner(struct nfsclowner *owp, int local) 1481191783Srmacklem{ 1482191783Srmacklem 1483191783Srmacklem LIST_REMOVE(owp, nfsow_list); 1484191783Srmacklem FREE((caddr_t)owp, M_NFSCLOWNER); 1485191783Srmacklem if (local) 1486191783Srmacklem newnfsstats.cllocalopenowners--; 1487191783Srmacklem else 1488191783Srmacklem newnfsstats.clopenowners--; 1489191783Srmacklem} 1490191783Srmacklem 1491191783Srmacklem/* 1492191783Srmacklem * Free up a byte range lock owner structure. 1493191783Srmacklem */ 1494223747SrmacklemAPPLESTATIC void 1495191783Srmacklemnfscl_freelockowner(struct nfscllockowner *lp, int local) 1496191783Srmacklem{ 1497191783Srmacklem struct nfscllock *lop, *nlop; 1498191783Srmacklem 1499191783Srmacklem LIST_REMOVE(lp, nfsl_list); 1500191783Srmacklem LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) { 1501191783Srmacklem nfscl_freelock(lop, local); 1502191783Srmacklem } 1503191783Srmacklem FREE((caddr_t)lp, M_NFSCLLOCKOWNER); 1504191783Srmacklem if (local) 1505191783Srmacklem newnfsstats.cllocallockowners--; 1506191783Srmacklem else 1507191783Srmacklem newnfsstats.cllockowners--; 1508191783Srmacklem} 1509191783Srmacklem 1510191783Srmacklem/* 1511191783Srmacklem * Free up a byte range lock structure. 1512191783Srmacklem */ 1513191783SrmacklemAPPLESTATIC void 1514191783Srmacklemnfscl_freelock(struct nfscllock *lop, int local) 1515191783Srmacklem{ 1516191783Srmacklem 1517191783Srmacklem LIST_REMOVE(lop, nfslo_list); 1518191783Srmacklem FREE((caddr_t)lop, M_NFSCLLOCK); 1519191783Srmacklem if (local) 1520191783Srmacklem newnfsstats.cllocallocks--; 1521191783Srmacklem else 1522191783Srmacklem newnfsstats.cllocks--; 1523191783Srmacklem} 1524191783Srmacklem 1525191783Srmacklem/* 1526191783Srmacklem * Clean out the state related to a delegation. 1527191783Srmacklem */ 1528191783Srmacklemstatic void 1529191783Srmacklemnfscl_cleandeleg(struct nfscldeleg *dp) 1530191783Srmacklem{ 1531191783Srmacklem struct nfsclowner *owp, *nowp; 1532191783Srmacklem struct nfsclopen *op; 1533191783Srmacklem 1534191783Srmacklem LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) { 1535191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1536191783Srmacklem if (op != NULL) { 1537191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 1538191783Srmacklem panic("nfscleandel"); 1539191783Srmacklem nfscl_freeopen(op, 1); 1540191783Srmacklem } 1541191783Srmacklem nfscl_freeopenowner(owp, 1); 1542191783Srmacklem } 1543191783Srmacklem nfscl_freealllocks(&dp->nfsdl_lock, 1); 1544191783Srmacklem} 1545191783Srmacklem 1546191783Srmacklem/* 1547191783Srmacklem * Free a delegation. 1548191783Srmacklem */ 1549191783Srmacklemstatic void 1550191783Srmacklemnfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp) 1551191783Srmacklem{ 1552191783Srmacklem 1553191783Srmacklem TAILQ_REMOVE(hdp, dp, nfsdl_list); 1554191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 1555191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 1556191783Srmacklem newnfsstats.cldelegates--; 1557191783Srmacklem nfscl_delegcnt--; 1558191783Srmacklem} 1559191783Srmacklem 1560191783Srmacklem/* 1561191783Srmacklem * Free up all state related to this client structure. 1562191783Srmacklem */ 1563191783Srmacklemstatic void 1564191783Srmacklemnfscl_cleanclient(struct nfsclclient *clp) 1565191783Srmacklem{ 1566191783Srmacklem struct nfsclowner *owp, *nowp; 1567191783Srmacklem struct nfsclopen *op, *nop; 1568191783Srmacklem 1569191783Srmacklem /* Now, all the OpenOwners, etc. */ 1570191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1571191783Srmacklem LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) { 1572191783Srmacklem nfscl_freeopen(op, 0); 1573191783Srmacklem } 1574191783Srmacklem nfscl_freeopenowner(owp, 0); 1575191783Srmacklem } 1576191783Srmacklem} 1577191783Srmacklem 1578191783Srmacklem/* 1579191783Srmacklem * Called when an NFSERR_EXPIRED is received from the server. 1580191783Srmacklem */ 1581191783Srmacklemstatic void 1582191783Srmacklemnfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp, 1583191783Srmacklem struct ucred *cred, NFSPROC_T *p) 1584191783Srmacklem{ 1585191783Srmacklem struct nfsclowner *owp, *nowp, *towp; 1586191783Srmacklem struct nfsclopen *op, *nop, *top; 1587191783Srmacklem struct nfscldeleg *dp, *ndp; 1588191783Srmacklem int ret, printed = 0; 1589191783Srmacklem 1590191783Srmacklem /* 1591191783Srmacklem * First, merge locally issued Opens into the list for the server. 1592191783Srmacklem */ 1593191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 1594191783Srmacklem while (dp != NULL) { 1595191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 1596191783Srmacklem owp = LIST_FIRST(&dp->nfsdl_owner); 1597191783Srmacklem while (owp != NULL) { 1598191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1599191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1600191783Srmacklem if (op != NULL) { 1601191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 1602191783Srmacklem panic("nfsclexp"); 1603191783Srmacklem LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) { 1604191783Srmacklem if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner, 1605191783Srmacklem NFSV4CL_LOCKNAMELEN)) 1606191783Srmacklem break; 1607191783Srmacklem } 1608191783Srmacklem if (towp != NULL) { 1609191783Srmacklem /* Merge opens in */ 1610191783Srmacklem LIST_FOREACH(top, &towp->nfsow_open, nfso_list) { 1611191783Srmacklem if (top->nfso_fhlen == op->nfso_fhlen && 1612191783Srmacklem !NFSBCMP(top->nfso_fh, op->nfso_fh, 1613191783Srmacklem op->nfso_fhlen)) { 1614191783Srmacklem top->nfso_mode |= op->nfso_mode; 1615191783Srmacklem top->nfso_opencnt += op->nfso_opencnt; 1616191783Srmacklem break; 1617191783Srmacklem } 1618191783Srmacklem } 1619191783Srmacklem if (top == NULL) { 1620191783Srmacklem /* Just add the open to the owner list */ 1621191783Srmacklem LIST_REMOVE(op, nfso_list); 1622191783Srmacklem op->nfso_own = towp; 1623191783Srmacklem LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list); 1624191783Srmacklem newnfsstats.cllocalopens--; 1625191783Srmacklem newnfsstats.clopens++; 1626191783Srmacklem } 1627191783Srmacklem } else { 1628191783Srmacklem /* Just add the openowner to the client list */ 1629191783Srmacklem LIST_REMOVE(owp, nfsow_list); 1630191783Srmacklem owp->nfsow_clp = clp; 1631191783Srmacklem LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list); 1632191783Srmacklem newnfsstats.cllocalopenowners--; 1633191783Srmacklem newnfsstats.clopenowners++; 1634191783Srmacklem newnfsstats.cllocalopens--; 1635191783Srmacklem newnfsstats.clopens++; 1636191783Srmacklem } 1637191783Srmacklem } 1638191783Srmacklem owp = nowp; 1639191783Srmacklem } 1640191783Srmacklem if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) { 1641191783Srmacklem printed = 1; 1642191783Srmacklem printf("nfsv4 expired locks lost\n"); 1643191783Srmacklem } 1644191783Srmacklem nfscl_cleandeleg(dp); 1645191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 1646191783Srmacklem dp = ndp; 1647191783Srmacklem } 1648191783Srmacklem if (!TAILQ_EMPTY(&clp->nfsc_deleg)) 1649191783Srmacklem panic("nfsclexp"); 1650191783Srmacklem 1651191783Srmacklem /* 1652191783Srmacklem * Now, try and reopen against the server. 1653191783Srmacklem */ 1654191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1655191783Srmacklem owp->nfsow_seqid = 0; 1656191783Srmacklem LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) { 1657191783Srmacklem ret = nfscl_expireopen(clp, op, nmp, cred, p); 1658191783Srmacklem if (ret && !printed) { 1659191783Srmacklem printed = 1; 1660191783Srmacklem printf("nfsv4 expired locks lost\n"); 1661191783Srmacklem } 1662191783Srmacklem } 1663191783Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) 1664191783Srmacklem nfscl_freeopenowner(owp, 0); 1665191783Srmacklem } 1666191783Srmacklem} 1667191783Srmacklem 1668191783Srmacklem/* 1669227744Srmacklem * This function must be called after the process represented by "own" has 1670227744Srmacklem * exited. Must be called with CLSTATE lock held. 1671191783Srmacklem */ 1672191783Srmacklemstatic void 1673191783Srmacklemnfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own) 1674191783Srmacklem{ 1675191783Srmacklem struct nfsclowner *owp, *nowp; 1676191783Srmacklem struct nfscllockowner *lp, *nlp; 1677191783Srmacklem struct nfscldeleg *dp; 1678191783Srmacklem 1679191783Srmacklem /* First, get rid of local locks on delegations. */ 1680191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 1681191783Srmacklem LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) { 1682191783Srmacklem if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) { 1683191783Srmacklem if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED)) 1684191783Srmacklem panic("nfscllckw"); 1685191783Srmacklem nfscl_freelockowner(lp, 1); 1686191783Srmacklem } 1687191783Srmacklem } 1688191783Srmacklem } 1689191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 1690191783Srmacklem while (owp != NULL) { 1691191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1692191783Srmacklem if (!NFSBCMP(owp->nfsow_owner, own, 1693191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 1694191783Srmacklem /* 1695191783Srmacklem * If there are children that haven't closed the 1696191783Srmacklem * file descriptors yet, the opens will still be 1697191783Srmacklem * here. For that case, let the renew thread clear 1698191783Srmacklem * out the OpenOwner later. 1699191783Srmacklem */ 1700191783Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) 1701191783Srmacklem nfscl_freeopenowner(owp, 0); 1702191783Srmacklem else 1703191783Srmacklem owp->nfsow_defunct = 1; 1704191783Srmacklem } 1705191783Srmacklem owp = nowp; 1706191783Srmacklem } 1707191783Srmacklem} 1708191783Srmacklem 1709191783Srmacklem/* 1710227744Srmacklem * Find open/lock owners for processes that have exited. 1711191783Srmacklem */ 1712191783Srmacklemstatic void 1713228217Srmacklemnfscl_cleanupkext(struct nfsclclient *clp, struct nfscllockownerfhhead *lhp) 1714191783Srmacklem{ 1715191783Srmacklem struct nfsclowner *owp, *nowp; 1716228217Srmacklem struct nfsclopen *op; 1717228217Srmacklem struct nfscllockowner *lp, *nlp; 1718191783Srmacklem 1719191783Srmacklem NFSPROCLISTLOCK(); 1720191783Srmacklem NFSLOCKCLSTATE(); 1721191783Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 1722228217Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 1723228217Srmacklem LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp) { 1724228217Srmacklem if (LIST_EMPTY(&lp->nfsl_lock)) 1725228217Srmacklem nfscl_emptylockowner(lp, lhp); 1726228217Srmacklem } 1727228217Srmacklem } 1728191783Srmacklem if (nfscl_procdoesntexist(owp->nfsow_owner)) 1729191783Srmacklem nfscl_cleanup_common(clp, owp->nfsow_owner); 1730191783Srmacklem } 1731191783Srmacklem NFSUNLOCKCLSTATE(); 1732191783Srmacklem NFSPROCLISTUNLOCK(); 1733191783Srmacklem} 1734191783Srmacklem 1735228217Srmacklem/* 1736228217Srmacklem * Take the empty lock owner and move it to the local lhp list if the 1737228217Srmacklem * associated process no longer exists. 1738228217Srmacklem */ 1739228217Srmacklemstatic void 1740228217Srmacklemnfscl_emptylockowner(struct nfscllockowner *lp, 1741228217Srmacklem struct nfscllockownerfhhead *lhp) 1742228217Srmacklem{ 1743228217Srmacklem struct nfscllockownerfh *lfhp, *mylfhp; 1744228217Srmacklem struct nfscllockowner *nlp; 1745228217Srmacklem int fnd_it; 1746228217Srmacklem 1747228217Srmacklem /* If not a Posix lock owner, just return. */ 1748228217Srmacklem if ((lp->nfsl_lockflags & F_POSIX) == 0) 1749228217Srmacklem return; 1750228217Srmacklem 1751228217Srmacklem fnd_it = 0; 1752228217Srmacklem mylfhp = NULL; 1753228217Srmacklem /* 1754228217Srmacklem * First, search to see if this lock owner is already in the list. 1755228217Srmacklem * If it is, then the associated process no longer exists. 1756228217Srmacklem */ 1757228217Srmacklem SLIST_FOREACH(lfhp, lhp, nfslfh_list) { 1758228217Srmacklem if (lfhp->nfslfh_len == lp->nfsl_open->nfso_fhlen && 1759228217Srmacklem !NFSBCMP(lfhp->nfslfh_fh, lp->nfsl_open->nfso_fh, 1760228217Srmacklem lfhp->nfslfh_len)) 1761228217Srmacklem mylfhp = lfhp; 1762228217Srmacklem LIST_FOREACH(nlp, &lfhp->nfslfh_lock, nfsl_list) 1763228217Srmacklem if (!NFSBCMP(nlp->nfsl_owner, lp->nfsl_owner, 1764228217Srmacklem NFSV4CL_LOCKNAMELEN)) 1765228217Srmacklem fnd_it = 1; 1766228217Srmacklem } 1767228217Srmacklem /* If not found, check if process still exists. */ 1768228217Srmacklem if (fnd_it == 0 && nfscl_procdoesntexist(lp->nfsl_owner) == 0) 1769228217Srmacklem return; 1770228217Srmacklem 1771228217Srmacklem /* Move the lock owner over to the local list. */ 1772228217Srmacklem if (mylfhp == NULL) { 1773228217Srmacklem mylfhp = malloc(sizeof(struct nfscllockownerfh), M_TEMP, 1774228217Srmacklem M_NOWAIT); 1775228217Srmacklem if (mylfhp == NULL) 1776228217Srmacklem return; 1777228217Srmacklem mylfhp->nfslfh_len = lp->nfsl_open->nfso_fhlen; 1778228217Srmacklem NFSBCOPY(lp->nfsl_open->nfso_fh, mylfhp->nfslfh_fh, 1779228217Srmacklem mylfhp->nfslfh_len); 1780228217Srmacklem LIST_INIT(&mylfhp->nfslfh_lock); 1781228217Srmacklem SLIST_INSERT_HEAD(lhp, mylfhp, nfslfh_list); 1782228217Srmacklem } 1783228217Srmacklem LIST_REMOVE(lp, nfsl_list); 1784228217Srmacklem LIST_INSERT_HEAD(&mylfhp->nfslfh_lock, lp, nfsl_list); 1785228217Srmacklem} 1786228217Srmacklem 1787222389Srmacklemstatic int fake_global; /* Used to force visibility of MNTK_UNMOUNTF */ 1788191783Srmacklem/* 1789191783Srmacklem * Called from nfs umount to free up the clientid. 1790191783Srmacklem */ 1791191783SrmacklemAPPLESTATIC void 1792191783Srmacklemnfscl_umount(struct nfsmount *nmp, NFSPROC_T *p) 1793191783Srmacklem{ 1794191783Srmacklem struct nfsclclient *clp; 1795191783Srmacklem struct ucred *cred; 1796191783Srmacklem int igotlock; 1797191783Srmacklem 1798222389Srmacklem /* 1799222389Srmacklem * For the case that matters, this is the thread that set 1800222389Srmacklem * MNTK_UNMOUNTF, so it will see it set. The code that follows is 1801222389Srmacklem * done to ensure that any thread executing nfscl_getcl() after 1802222389Srmacklem * this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the 1803222389Srmacklem * mutex for NFSLOCKCLSTATE(), so it is "m" for the following 1804222389Srmacklem * explanation, courtesy of Alan Cox. 1805222389Srmacklem * What follows is a snippet from Alan Cox's email at: 1806222389Srmacklem * http://docs.FreeBSD.org/cgi/ 1807222389Srmacklem * mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw 1808222389Srmacklem * 1809222389Srmacklem * 1. Set MNTK_UNMOUNTF 1810222389Srmacklem * 2. Acquire a standard FreeBSD mutex "m". 1811222389Srmacklem * 3. Update some data structures. 1812222389Srmacklem * 4. Release mutex "m". 1813222389Srmacklem * 1814222389Srmacklem * Then, other threads that acquire "m" after step 4 has occurred will 1815222389Srmacklem * see MNTK_UNMOUNTF as set. But, other threads that beat thread X to 1816222389Srmacklem * step 2 may or may not see MNTK_UNMOUNTF as set. 1817222389Srmacklem */ 1818222389Srmacklem NFSLOCKCLSTATE(); 1819222389Srmacklem if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 1820222389Srmacklem fake_global++; 1821222389Srmacklem NFSUNLOCKCLSTATE(); 1822222389Srmacklem NFSLOCKCLSTATE(); 1823222389Srmacklem } 1824222389Srmacklem 1825191783Srmacklem clp = nmp->nm_clp; 1826191783Srmacklem if (clp != NULL) { 1827191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0) 1828191783Srmacklem panic("nfscl umount"); 1829191783Srmacklem 1830191783Srmacklem /* 1831191783Srmacklem * First, handshake with the nfscl renew thread, to terminate 1832191783Srmacklem * it. 1833191783Srmacklem */ 1834191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_UMOUNT; 1835191783Srmacklem while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD) 1836222389Srmacklem (void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, 1837222389Srmacklem "nfsclumnt", hz); 1838191783Srmacklem 1839222389Srmacklem /* 1840222389Srmacklem * Now, get the exclusive lock on the client state, so 1841222389Srmacklem * that no uses of the state are still in progress. 1842222389Srmacklem */ 1843191783Srmacklem do { 1844191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 1845222389Srmacklem NFSCLSTATEMUTEXPTR, NULL); 1846191783Srmacklem } while (!igotlock); 1847191783Srmacklem NFSUNLOCKCLSTATE(); 1848191783Srmacklem 1849191783Srmacklem /* 1850191783Srmacklem * Free up all the state. It will expire on the server, but 1851191783Srmacklem * maybe we should do a SetClientId/SetClientIdConfirm so 1852191783Srmacklem * the server throws it away? 1853191783Srmacklem */ 1854191783Srmacklem LIST_REMOVE(clp, nfsc_list); 1855191783Srmacklem nfscl_delegreturnall(clp, p); 1856191783Srmacklem cred = newnfs_getcred(); 1857244042Srmacklem if (NFSHASNFSV4N(nmp)) { 1858244042Srmacklem (void)nfsrpc_destroysession(nmp, clp, cred, p); 1859244042Srmacklem (void)nfsrpc_destroyclient(nmp, clp, cred, p); 1860244042Srmacklem } else 1861244042Srmacklem (void)nfsrpc_setclient(nmp, clp, 0, cred, p); 1862191783Srmacklem nfscl_cleanclient(clp); 1863191783Srmacklem nmp->nm_clp = NULL; 1864191783Srmacklem NFSFREECRED(cred); 1865244042Srmacklem free(clp, M_NFSCLCLIENT); 1866222389Srmacklem } else 1867222389Srmacklem NFSUNLOCKCLSTATE(); 1868191783Srmacklem} 1869191783Srmacklem 1870191783Srmacklem/* 1871191783Srmacklem * This function is called when a server replies with NFSERR_STALECLIENTID 1872244042Srmacklem * NFSERR_STALESTATEID or NFSERR_BADSESSION. It traverses the clientid lists, 1873244042Srmacklem * doing Opens and Locks with reclaim. If these fail, it deletes the 1874244042Srmacklem * corresponding state. 1875191783Srmacklem */ 1876191783Srmacklemstatic void 1877191783Srmacklemnfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p) 1878191783Srmacklem{ 1879191783Srmacklem struct nfsclowner *owp, *nowp; 1880191783Srmacklem struct nfsclopen *op, *nop; 1881191783Srmacklem struct nfscllockowner *lp, *nlp; 1882191783Srmacklem struct nfscllock *lop, *nlop; 1883191783Srmacklem struct nfscldeleg *dp, *ndp, *tdp; 1884191783Srmacklem struct nfsmount *nmp; 1885191783Srmacklem struct ucred *tcred; 1886191783Srmacklem struct nfsclopenhead extra_open; 1887191783Srmacklem struct nfscldeleghead extra_deleg; 1888191783Srmacklem struct nfsreq *rep; 1889191783Srmacklem u_int64_t len; 1890191783Srmacklem u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode; 1891247072Simp int i, igotlock = 0, error, trycnt, firstlock; 1892244042Srmacklem struct nfscllayout *lyp, *nlyp; 1893191783Srmacklem 1894191783Srmacklem /* 1895191783Srmacklem * First, lock the client structure, so everyone else will 1896191783Srmacklem * block when trying to use state. 1897191783Srmacklem */ 1898191783Srmacklem NFSLOCKCLSTATE(); 1899206818Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG; 1900191783Srmacklem do { 1901191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 1902222389Srmacklem NFSCLSTATEMUTEXPTR, NULL); 1903191783Srmacklem } while (!igotlock); 1904191783Srmacklem NFSUNLOCKCLSTATE(); 1905191783Srmacklem 1906191783Srmacklem nmp = clp->nfsc_nmp; 1907191783Srmacklem if (nmp == NULL) 1908191783Srmacklem panic("nfscl recover"); 1909244042Srmacklem 1910244042Srmacklem /* 1911244042Srmacklem * For now, just get rid of all layouts. There may be a need 1912244042Srmacklem * to do LayoutCommit Ops with reclaim == true later. 1913244042Srmacklem */ 1914244042Srmacklem TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) 1915244042Srmacklem nfscl_freelayout(lyp); 1916244042Srmacklem TAILQ_INIT(&clp->nfsc_layout); 1917244042Srmacklem for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++) 1918244042Srmacklem LIST_INIT(&clp->nfsc_layouthash[i]); 1919244042Srmacklem 1920191783Srmacklem trycnt = 5; 1921191783Srmacklem do { 1922244042Srmacklem error = nfsrpc_setclient(nmp, clp, 1, cred, p); 1923191783Srmacklem } while ((error == NFSERR_STALECLIENTID || 1924244042Srmacklem error == NFSERR_BADSESSION || 1925191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); 1926191783Srmacklem if (error) { 1927191783Srmacklem nfscl_cleanclient(clp); 1928206818Srmacklem NFSLOCKCLSTATE(); 1929191783Srmacklem clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID | 1930206818Srmacklem NFSCLFLAGS_RECOVER | NFSCLFLAGS_RECVRINPROG); 1931206818Srmacklem wakeup(&clp->nfsc_flags); 1932191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 1933191783Srmacklem NFSUNLOCKCLSTATE(); 1934191783Srmacklem return; 1935191783Srmacklem } 1936191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 1937191783Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER; 1938191783Srmacklem 1939191783Srmacklem /* 1940191783Srmacklem * Mark requests already queued on the server, so that they don't 1941191783Srmacklem * initiate another recovery cycle. Any requests already in the 1942191783Srmacklem * queue that handle state information will have the old stale 1943244042Srmacklem * clientid/stateid and will get a NFSERR_STALESTATEID, 1944244042Srmacklem * NFSERR_STALECLIENTID or NFSERR_BADSESSION reply from the server. 1945244042Srmacklem * This will be translated to NFSERR_STALEDONTRECOVER when 1946244042Srmacklem * R_DONTRECOVER is set. 1947191783Srmacklem */ 1948191783Srmacklem NFSLOCKREQ(); 1949191783Srmacklem TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) { 1950191783Srmacklem if (rep->r_nmp == nmp) 1951191783Srmacklem rep->r_flags |= R_DONTRECOVER; 1952191783Srmacklem } 1953191783Srmacklem NFSUNLOCKREQ(); 1954191783Srmacklem 1955191783Srmacklem /* 1956191783Srmacklem * Now, mark all delegations "need reclaim". 1957191783Srmacklem */ 1958191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) 1959191783Srmacklem dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM; 1960191783Srmacklem 1961191783Srmacklem TAILQ_INIT(&extra_deleg); 1962191783Srmacklem LIST_INIT(&extra_open); 1963191783Srmacklem /* 1964191783Srmacklem * Now traverse the state lists, doing Open and Lock Reclaims. 1965191783Srmacklem */ 1966191783Srmacklem tcred = newnfs_getcred(); 1967191783Srmacklem owp = LIST_FIRST(&clp->nfsc_owner); 1968191783Srmacklem while (owp != NULL) { 1969191783Srmacklem nowp = LIST_NEXT(owp, nfsow_list); 1970191783Srmacklem owp->nfsow_seqid = 0; 1971191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 1972191783Srmacklem while (op != NULL) { 1973191783Srmacklem nop = LIST_NEXT(op, nfso_list); 1974191783Srmacklem if (error != NFSERR_NOGRACE) { 1975191783Srmacklem /* Search for a delegation to reclaim with the open */ 1976191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 1977191783Srmacklem if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) 1978191783Srmacklem continue; 1979191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE)) { 1980191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 1981191783Srmacklem delegtype = NFSV4OPEN_DELEGATEWRITE; 1982191783Srmacklem } else { 1983191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 1984191783Srmacklem delegtype = NFSV4OPEN_DELEGATEREAD; 1985191783Srmacklem } 1986191783Srmacklem if ((op->nfso_mode & mode) == mode && 1987191783Srmacklem op->nfso_fhlen == dp->nfsdl_fhlen && 1988191783Srmacklem !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen)) 1989191783Srmacklem break; 1990191783Srmacklem } 1991191783Srmacklem ndp = dp; 1992191783Srmacklem if (dp == NULL) 1993191783Srmacklem delegtype = NFSV4OPEN_DELEGATENONE; 1994191783Srmacklem newnfs_copycred(&op->nfso_cred, tcred); 1995191783Srmacklem error = nfscl_tryopen(nmp, NULL, op->nfso_fh, 1996191783Srmacklem op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen, 1997191783Srmacklem op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype, 1998191783Srmacklem tcred, p); 1999191783Srmacklem if (!error) { 2000191783Srmacklem /* Handle any replied delegation */ 2001191783Srmacklem if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE) 2002191783Srmacklem || NFSMNT_RDONLY(nmp->nm_mountp))) { 2003191783Srmacklem if ((ndp->nfsdl_flags & NFSCLDL_WRITE)) 2004191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 2005191783Srmacklem else 2006191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 2007191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 2008191783Srmacklem if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) 2009191783Srmacklem continue; 2010191783Srmacklem if ((op->nfso_mode & mode) == mode && 2011191783Srmacklem op->nfso_fhlen == dp->nfsdl_fhlen && 2012191783Srmacklem !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, 2013191783Srmacklem op->nfso_fhlen)) { 2014191783Srmacklem dp->nfsdl_stateid = ndp->nfsdl_stateid; 2015191783Srmacklem dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit; 2016191783Srmacklem dp->nfsdl_ace = ndp->nfsdl_ace; 2017191783Srmacklem dp->nfsdl_change = ndp->nfsdl_change; 2018191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM; 2019191783Srmacklem if ((ndp->nfsdl_flags & NFSCLDL_RECALL)) 2020191783Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 2021191783Srmacklem FREE((caddr_t)ndp, M_NFSCLDELEG); 2022191783Srmacklem ndp = NULL; 2023191783Srmacklem break; 2024191783Srmacklem } 2025191783Srmacklem } 2026191783Srmacklem } 2027191783Srmacklem if (ndp != NULL) 2028191783Srmacklem TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list); 2029191783Srmacklem 2030191783Srmacklem /* and reclaim all byte range locks */ 2031191783Srmacklem lp = LIST_FIRST(&op->nfso_lock); 2032191783Srmacklem while (lp != NULL) { 2033191783Srmacklem nlp = LIST_NEXT(lp, nfsl_list); 2034191783Srmacklem lp->nfsl_seqid = 0; 2035191783Srmacklem firstlock = 1; 2036191783Srmacklem lop = LIST_FIRST(&lp->nfsl_lock); 2037191783Srmacklem while (lop != NULL) { 2038191783Srmacklem nlop = LIST_NEXT(lop, nfslo_list); 2039191783Srmacklem if (lop->nfslo_end == NFS64BITSSET) 2040191783Srmacklem len = NFS64BITSSET; 2041191783Srmacklem else 2042191783Srmacklem len = lop->nfslo_end - lop->nfslo_first; 2043191783Srmacklem if (error != NFSERR_NOGRACE) 2044191783Srmacklem error = nfscl_trylock(nmp, NULL, 2045191783Srmacklem op->nfso_fh, op->nfso_fhlen, lp, 2046191783Srmacklem firstlock, 1, lop->nfslo_first, len, 2047191783Srmacklem lop->nfslo_type, tcred, p); 2048191783Srmacklem if (error != 0) 2049191783Srmacklem nfscl_freelock(lop, 0); 2050191783Srmacklem else 2051191783Srmacklem firstlock = 0; 2052191783Srmacklem lop = nlop; 2053191783Srmacklem } 2054191783Srmacklem /* If no locks, but a lockowner, just delete it. */ 2055191783Srmacklem if (LIST_EMPTY(&lp->nfsl_lock)) 2056191783Srmacklem nfscl_freelockowner(lp, 0); 2057191783Srmacklem lp = nlp; 2058191783Srmacklem } 2059191783Srmacklem } else { 2060191783Srmacklem nfscl_freeopen(op, 0); 2061191783Srmacklem } 2062191783Srmacklem } 2063191783Srmacklem op = nop; 2064191783Srmacklem } 2065191783Srmacklem owp = nowp; 2066191783Srmacklem } 2067191783Srmacklem 2068191783Srmacklem /* 2069191783Srmacklem * Now, try and get any delegations not yet reclaimed by cobbling 2070191783Srmacklem * to-gether an appropriate open. 2071191783Srmacklem */ 2072191783Srmacklem nowp = NULL; 2073191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 2074191783Srmacklem while (dp != NULL) { 2075191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 2076191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) { 2077191783Srmacklem if (nowp == NULL) { 2078191783Srmacklem MALLOC(nowp, struct nfsclowner *, 2079191783Srmacklem sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK); 2080191783Srmacklem /* 2081191783Srmacklem * Name must be as long an largest possible 2082191783Srmacklem * NFSV4CL_LOCKNAMELEN. 12 for now. 2083191783Srmacklem */ 2084191783Srmacklem NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner, 2085191783Srmacklem NFSV4CL_LOCKNAMELEN); 2086191783Srmacklem LIST_INIT(&nowp->nfsow_open); 2087191783Srmacklem nowp->nfsow_clp = clp; 2088191783Srmacklem nowp->nfsow_seqid = 0; 2089191783Srmacklem nowp->nfsow_defunct = 0; 2090191783Srmacklem nfscl_lockinit(&nowp->nfsow_rwlock); 2091191783Srmacklem } 2092191783Srmacklem nop = NULL; 2093191783Srmacklem if (error != NFSERR_NOGRACE) { 2094191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 2095191783Srmacklem dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK); 2096191783Srmacklem nop->nfso_own = nowp; 2097191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE)) { 2098191783Srmacklem nop->nfso_mode = NFSV4OPEN_ACCESSWRITE; 2099191783Srmacklem delegtype = NFSV4OPEN_DELEGATEWRITE; 2100191783Srmacklem } else { 2101191783Srmacklem nop->nfso_mode = NFSV4OPEN_ACCESSREAD; 2102191783Srmacklem delegtype = NFSV4OPEN_DELEGATEREAD; 2103191783Srmacklem } 2104191783Srmacklem nop->nfso_opencnt = 0; 2105191783Srmacklem nop->nfso_posixlock = 1; 2106191783Srmacklem nop->nfso_fhlen = dp->nfsdl_fhlen; 2107191783Srmacklem NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen); 2108191783Srmacklem LIST_INIT(&nop->nfso_lock); 2109191783Srmacklem nop->nfso_stateid.seqid = 0; 2110191783Srmacklem nop->nfso_stateid.other[0] = 0; 2111191783Srmacklem nop->nfso_stateid.other[1] = 0; 2112191783Srmacklem nop->nfso_stateid.other[2] = 0; 2113191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, tcred); 2114191783Srmacklem newnfs_copyincred(tcred, &nop->nfso_cred); 2115191783Srmacklem tdp = NULL; 2116191783Srmacklem error = nfscl_tryopen(nmp, NULL, nop->nfso_fh, 2117191783Srmacklem nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen, 2118191783Srmacklem nop->nfso_mode, nop, NULL, 0, &tdp, 1, 2119191783Srmacklem delegtype, tcred, p); 2120191783Srmacklem if (tdp != NULL) { 2121191783Srmacklem if ((tdp->nfsdl_flags & NFSCLDL_WRITE)) 2122191783Srmacklem mode = NFSV4OPEN_ACCESSWRITE; 2123191783Srmacklem else 2124191783Srmacklem mode = NFSV4OPEN_ACCESSREAD; 2125191783Srmacklem if ((nop->nfso_mode & mode) == mode && 2126191783Srmacklem nop->nfso_fhlen == tdp->nfsdl_fhlen && 2127191783Srmacklem !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh, 2128191783Srmacklem nop->nfso_fhlen)) { 2129191783Srmacklem dp->nfsdl_stateid = tdp->nfsdl_stateid; 2130191783Srmacklem dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit; 2131191783Srmacklem dp->nfsdl_ace = tdp->nfsdl_ace; 2132191783Srmacklem dp->nfsdl_change = tdp->nfsdl_change; 2133191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM; 2134191783Srmacklem if ((tdp->nfsdl_flags & NFSCLDL_RECALL)) 2135191783Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 2136191783Srmacklem FREE((caddr_t)tdp, M_NFSCLDELEG); 2137191783Srmacklem } else { 2138191783Srmacklem TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list); 2139191783Srmacklem } 2140191783Srmacklem } 2141191783Srmacklem } 2142191783Srmacklem if (error) { 2143191783Srmacklem if (nop != NULL) 2144191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 2145191783Srmacklem /* 2146191783Srmacklem * Couldn't reclaim it, so throw the state 2147191783Srmacklem * away. Ouch!! 2148191783Srmacklem */ 2149191783Srmacklem nfscl_cleandeleg(dp); 2150191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 2151191783Srmacklem } else { 2152191783Srmacklem LIST_INSERT_HEAD(&extra_open, nop, nfso_list); 2153191783Srmacklem } 2154191783Srmacklem } 2155191783Srmacklem dp = ndp; 2156191783Srmacklem } 2157191783Srmacklem 2158191783Srmacklem /* 2159191783Srmacklem * Now, get rid of extra Opens and Delegations. 2160191783Srmacklem */ 2161191783Srmacklem LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) { 2162191783Srmacklem do { 2163191783Srmacklem newnfs_copycred(&op->nfso_cred, tcred); 2164191783Srmacklem error = nfscl_tryclose(op, tcred, nmp, p); 2165191783Srmacklem if (error == NFSERR_GRACE) 2166207170Srmacklem (void) nfs_catnap(PZERO, error, "nfsexcls"); 2167191783Srmacklem } while (error == NFSERR_GRACE); 2168191783Srmacklem LIST_REMOVE(op, nfso_list); 2169191783Srmacklem FREE((caddr_t)op, M_NFSCLOPEN); 2170191783Srmacklem } 2171191783Srmacklem if (nowp != NULL) 2172191783Srmacklem FREE((caddr_t)nowp, M_NFSCLOWNER); 2173191783Srmacklem 2174191783Srmacklem TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) { 2175191783Srmacklem do { 2176191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, tcred); 2177191783Srmacklem error = nfscl_trydelegreturn(dp, tcred, nmp, p); 2178191783Srmacklem if (error == NFSERR_GRACE) 2179207170Srmacklem (void) nfs_catnap(PZERO, error, "nfsexdlg"); 2180191783Srmacklem } while (error == NFSERR_GRACE); 2181191783Srmacklem TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list); 2182191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 2183191783Srmacklem } 2184191783Srmacklem 2185244042Srmacklem /* For NFSv4.1 or later, do a RECLAIM_COMPLETE. */ 2186244042Srmacklem if (NFSHASNFSV4N(nmp)) 2187244042Srmacklem (void)nfsrpc_reclaimcomplete(nmp, cred, p); 2188244042Srmacklem 2189191783Srmacklem NFSLOCKCLSTATE(); 2190206818Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG; 2191206818Srmacklem wakeup(&clp->nfsc_flags); 2192191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2193191783Srmacklem NFSUNLOCKCLSTATE(); 2194191783Srmacklem NFSFREECRED(tcred); 2195191783Srmacklem} 2196191783Srmacklem 2197191783Srmacklem/* 2198191783Srmacklem * This function is called when a server replies with NFSERR_EXPIRED. 2199191783Srmacklem * It deletes all state for the client and does a fresh SetClientId/confirm. 2200191783Srmacklem * XXX Someday it should post a signal to the process(es) that hold the 2201191783Srmacklem * state, so they know that lock state has been lost. 2202191783Srmacklem */ 2203191783SrmacklemAPPLESTATIC int 2204191783Srmacklemnfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p) 2205191783Srmacklem{ 2206191783Srmacklem struct nfsmount *nmp; 2207191783Srmacklem struct ucred *cred; 2208191783Srmacklem int igotlock = 0, error, trycnt; 2209191783Srmacklem 2210191783Srmacklem /* 2211191783Srmacklem * If the clientid has gone away or a new SetClientid has already 2212191783Srmacklem * been done, just return ok. 2213191783Srmacklem */ 2214191783Srmacklem if (clp == NULL || clidrev != clp->nfsc_clientidrev) 2215191783Srmacklem return (0); 2216191783Srmacklem 2217191783Srmacklem /* 2218191783Srmacklem * First, lock the client structure, so everyone else will 2219191783Srmacklem * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so 2220191783Srmacklem * that only one thread does the work. 2221191783Srmacklem */ 2222191783Srmacklem NFSLOCKCLSTATE(); 2223191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT; 2224191783Srmacklem do { 2225191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, 2226222389Srmacklem NFSCLSTATEMUTEXPTR, NULL); 2227191783Srmacklem } while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT)); 2228191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) { 2229191783Srmacklem if (igotlock) 2230191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2231191783Srmacklem NFSUNLOCKCLSTATE(); 2232191783Srmacklem return (0); 2233191783Srmacklem } 2234206880Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG; 2235191783Srmacklem NFSUNLOCKCLSTATE(); 2236191783Srmacklem 2237191783Srmacklem nmp = clp->nfsc_nmp; 2238191783Srmacklem if (nmp == NULL) 2239191783Srmacklem panic("nfscl expired"); 2240191783Srmacklem cred = newnfs_getcred(); 2241191783Srmacklem trycnt = 5; 2242191783Srmacklem do { 2243244042Srmacklem error = nfsrpc_setclient(nmp, clp, 0, cred, p); 2244191783Srmacklem } while ((error == NFSERR_STALECLIENTID || 2245244042Srmacklem error == NFSERR_BADSESSION || 2246191783Srmacklem error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); 2247191783Srmacklem if (error) { 2248191783Srmacklem /* 2249191783Srmacklem * Clear out any state. 2250191783Srmacklem */ 2251191783Srmacklem nfscl_cleanclient(clp); 2252206880Srmacklem NFSLOCKCLSTATE(); 2253191783Srmacklem clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID | 2254191783Srmacklem NFSCLFLAGS_RECOVER); 2255191783Srmacklem } else { 2256191783Srmacklem /* 2257191783Srmacklem * Expire the state for the client. 2258191783Srmacklem */ 2259191783Srmacklem nfscl_expireclient(clp, nmp, cred, p); 2260206880Srmacklem NFSLOCKCLSTATE(); 2261191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID; 2262191783Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER; 2263191783Srmacklem } 2264206880Srmacklem clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG); 2265206880Srmacklem wakeup(&clp->nfsc_flags); 2266191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2267191783Srmacklem NFSUNLOCKCLSTATE(); 2268206880Srmacklem NFSFREECRED(cred); 2269191783Srmacklem return (error); 2270191783Srmacklem} 2271191783Srmacklem 2272191783Srmacklem/* 2273191783Srmacklem * This function inserts a lock in the list after insert_lop. 2274191783Srmacklem */ 2275191783Srmacklemstatic void 2276191783Srmacklemnfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop, 2277191783Srmacklem struct nfscllock *insert_lop, int local) 2278191783Srmacklem{ 2279191783Srmacklem 2280191783Srmacklem if ((struct nfscllockowner *)insert_lop == lp) 2281191783Srmacklem LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list); 2282191783Srmacklem else 2283191783Srmacklem LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list); 2284191783Srmacklem if (local) 2285191783Srmacklem newnfsstats.cllocallocks++; 2286191783Srmacklem else 2287191783Srmacklem newnfsstats.cllocks++; 2288191783Srmacklem} 2289191783Srmacklem 2290191783Srmacklem/* 2291191783Srmacklem * This function updates the locking for a lock owner and given file. It 2292191783Srmacklem * maintains a list of lock ranges ordered on increasing file offset that 2293191783Srmacklem * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style). 2294191783Srmacklem * It always adds new_lop to the list and sometimes uses the one pointed 2295191783Srmacklem * at by other_lopp. 2296191783Srmacklem * Returns 1 if the locks were modified, 0 otherwise. 2297191783Srmacklem */ 2298191783Srmacklemstatic int 2299191783Srmacklemnfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp, 2300191783Srmacklem struct nfscllock **other_lopp, int local) 2301191783Srmacklem{ 2302191783Srmacklem struct nfscllock *new_lop = *new_lopp; 2303191783Srmacklem struct nfscllock *lop, *tlop, *ilop; 2304191783Srmacklem struct nfscllock *other_lop; 2305191783Srmacklem int unlock = 0, modified = 0; 2306191783Srmacklem u_int64_t tmp; 2307191783Srmacklem 2308191783Srmacklem /* 2309191783Srmacklem * Work down the list until the lock is merged. 2310191783Srmacklem */ 2311191783Srmacklem if (new_lop->nfslo_type == F_UNLCK) 2312191783Srmacklem unlock = 1; 2313191783Srmacklem ilop = (struct nfscllock *)lp; 2314191783Srmacklem lop = LIST_FIRST(&lp->nfsl_lock); 2315191783Srmacklem while (lop != NULL) { 2316191783Srmacklem /* 2317191783Srmacklem * Only check locks for this file that aren't before the start of 2318191783Srmacklem * new lock's range. 2319191783Srmacklem */ 2320191783Srmacklem if (lop->nfslo_end >= new_lop->nfslo_first) { 2321191783Srmacklem if (new_lop->nfslo_end < lop->nfslo_first) { 2322191783Srmacklem /* 2323191783Srmacklem * If the new lock ends before the start of the 2324191783Srmacklem * current lock's range, no merge, just insert 2325191783Srmacklem * the new lock. 2326191783Srmacklem */ 2327191783Srmacklem break; 2328191783Srmacklem } 2329191783Srmacklem if (new_lop->nfslo_type == lop->nfslo_type || 2330191783Srmacklem (new_lop->nfslo_first <= lop->nfslo_first && 2331191783Srmacklem new_lop->nfslo_end >= lop->nfslo_end)) { 2332191783Srmacklem /* 2333191783Srmacklem * This lock can be absorbed by the new lock/unlock. 2334191783Srmacklem * This happens when it covers the entire range 2335191783Srmacklem * of the old lock or is contiguous 2336191783Srmacklem * with the old lock and is of the same type or an 2337191783Srmacklem * unlock. 2338191783Srmacklem */ 2339191783Srmacklem if (new_lop->nfslo_type != lop->nfslo_type || 2340191783Srmacklem new_lop->nfslo_first != lop->nfslo_first || 2341191783Srmacklem new_lop->nfslo_end != lop->nfslo_end) 2342191783Srmacklem modified = 1; 2343191783Srmacklem if (lop->nfslo_first < new_lop->nfslo_first) 2344191783Srmacklem new_lop->nfslo_first = lop->nfslo_first; 2345191783Srmacklem if (lop->nfslo_end > new_lop->nfslo_end) 2346191783Srmacklem new_lop->nfslo_end = lop->nfslo_end; 2347191783Srmacklem tlop = lop; 2348191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2349191783Srmacklem nfscl_freelock(tlop, local); 2350191783Srmacklem continue; 2351191783Srmacklem } 2352191783Srmacklem 2353191783Srmacklem /* 2354191783Srmacklem * All these cases are for contiguous locks that are not the 2355191783Srmacklem * same type, so they can't be merged. 2356191783Srmacklem */ 2357191783Srmacklem if (new_lop->nfslo_first <= lop->nfslo_first) { 2358191783Srmacklem /* 2359191783Srmacklem * This case is where the new lock overlaps with the 2360191783Srmacklem * first part of the old lock. Move the start of the 2361191783Srmacklem * old lock to just past the end of the new lock. The 2362191783Srmacklem * new lock will be inserted in front of the old, since 2363191783Srmacklem * ilop hasn't been updated. (We are done now.) 2364191783Srmacklem */ 2365191783Srmacklem if (lop->nfslo_first != new_lop->nfslo_end) { 2366191783Srmacklem lop->nfslo_first = new_lop->nfslo_end; 2367191783Srmacklem modified = 1; 2368191783Srmacklem } 2369191783Srmacklem break; 2370191783Srmacklem } 2371191783Srmacklem if (new_lop->nfslo_end >= lop->nfslo_end) { 2372191783Srmacklem /* 2373191783Srmacklem * This case is where the new lock overlaps with the 2374191783Srmacklem * end of the old lock's range. Move the old lock's 2375191783Srmacklem * end to just before the new lock's first and insert 2376191783Srmacklem * the new lock after the old lock. 2377191783Srmacklem * Might not be done yet, since the new lock could 2378191783Srmacklem * overlap further locks with higher ranges. 2379191783Srmacklem */ 2380191783Srmacklem if (lop->nfslo_end != new_lop->nfslo_first) { 2381191783Srmacklem lop->nfslo_end = new_lop->nfslo_first; 2382191783Srmacklem modified = 1; 2383191783Srmacklem } 2384191783Srmacklem ilop = lop; 2385191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2386191783Srmacklem continue; 2387191783Srmacklem } 2388191783Srmacklem /* 2389191783Srmacklem * The final case is where the new lock's range is in the 2390191783Srmacklem * middle of the current lock's and splits the current lock 2391191783Srmacklem * up. Use *other_lopp to handle the second part of the 2392191783Srmacklem * split old lock range. (We are done now.) 2393191783Srmacklem * For unlock, we use new_lop as other_lop and tmp, since 2394191783Srmacklem * other_lop and new_lop are the same for this case. 2395191783Srmacklem * We noted the unlock case above, so we don't need 2396191783Srmacklem * new_lop->nfslo_type any longer. 2397191783Srmacklem */ 2398191783Srmacklem tmp = new_lop->nfslo_first; 2399191783Srmacklem if (unlock) { 2400191783Srmacklem other_lop = new_lop; 2401191783Srmacklem *new_lopp = NULL; 2402191783Srmacklem } else { 2403191783Srmacklem other_lop = *other_lopp; 2404191783Srmacklem *other_lopp = NULL; 2405191783Srmacklem } 2406191783Srmacklem other_lop->nfslo_first = new_lop->nfslo_end; 2407191783Srmacklem other_lop->nfslo_end = lop->nfslo_end; 2408191783Srmacklem other_lop->nfslo_type = lop->nfslo_type; 2409191783Srmacklem lop->nfslo_end = tmp; 2410191783Srmacklem nfscl_insertlock(lp, other_lop, lop, local); 2411191783Srmacklem ilop = lop; 2412191783Srmacklem modified = 1; 2413191783Srmacklem break; 2414191783Srmacklem } 2415191783Srmacklem ilop = lop; 2416191783Srmacklem lop = LIST_NEXT(lop, nfslo_list); 2417191783Srmacklem if (lop == NULL) 2418191783Srmacklem break; 2419191783Srmacklem } 2420191783Srmacklem 2421191783Srmacklem /* 2422191783Srmacklem * Insert the new lock in the list at the appropriate place. 2423191783Srmacklem */ 2424191783Srmacklem if (!unlock) { 2425191783Srmacklem nfscl_insertlock(lp, new_lop, ilop, local); 2426191783Srmacklem *new_lopp = NULL; 2427191783Srmacklem modified = 1; 2428191783Srmacklem } 2429191783Srmacklem return (modified); 2430191783Srmacklem} 2431191783Srmacklem 2432191783Srmacklem/* 2433191783Srmacklem * This function must be run as a kernel thread. 2434191783Srmacklem * It does Renew Ops and recovery, when required. 2435191783Srmacklem */ 2436191783SrmacklemAPPLESTATIC void 2437191783Srmacklemnfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p) 2438191783Srmacklem{ 2439191783Srmacklem struct nfsclowner *owp, *nowp; 2440191783Srmacklem struct nfsclopen *op; 2441228217Srmacklem struct nfscllockowner *lp, *nlp; 2442191783Srmacklem struct nfscldeleghead dh; 2443191783Srmacklem struct nfscldeleg *dp, *ndp; 2444191783Srmacklem struct ucred *cred; 2445191783Srmacklem u_int32_t clidrev; 2446191783Srmacklem int error, cbpathdown, islept, igotlock, ret, clearok; 2447206818Srmacklem uint32_t recover_done_time = 0; 2448245909Sjhb time_t mytime; 2449227796Srmacklem static time_t prevsec = 0; 2450228217Srmacklem struct nfscllockownerfh *lfhp, *nlfhp; 2451228217Srmacklem struct nfscllockownerfhhead lfh; 2452244042Srmacklem struct nfscllayout *lyp, *nlyp; 2453244042Srmacklem struct nfscldevinfo *dip, *ndip; 2454244042Srmacklem struct nfscllayouthead rlh; 2455244042Srmacklem struct nfsclrecalllayout *recallp; 2456244042Srmacklem struct nfsclds *dsp; 2457191783Srmacklem 2458191783Srmacklem cred = newnfs_getcred(); 2459206690Srmacklem NFSLOCKCLSTATE(); 2460191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD; 2461206690Srmacklem NFSUNLOCKCLSTATE(); 2462191783Srmacklem for(;;) { 2463191783Srmacklem newnfs_setroot(cred); 2464191783Srmacklem cbpathdown = 0; 2465206818Srmacklem if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) { 2466206818Srmacklem /* 2467206818Srmacklem * Only allow one recover within 1/2 of the lease 2468206818Srmacklem * duration (nfsc_renew). 2469206818Srmacklem */ 2470206818Srmacklem if (recover_done_time < NFSD_MONOSEC) { 2471206818Srmacklem recover_done_time = NFSD_MONOSEC + 2472206818Srmacklem clp->nfsc_renew; 2473206818Srmacklem nfscl_recover(clp, cred, p); 2474206818Srmacklem } else { 2475206818Srmacklem NFSLOCKCLSTATE(); 2476206818Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER; 2477206818Srmacklem NFSUNLOCKCLSTATE(); 2478206818Srmacklem } 2479206818Srmacklem } 2480191783Srmacklem if (clp->nfsc_expire <= NFSD_MONOSEC && 2481191783Srmacklem (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) { 2482191783Srmacklem clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew; 2483191783Srmacklem clidrev = clp->nfsc_clientidrev; 2484244042Srmacklem error = nfsrpc_renew(clp, 2485244042Srmacklem TAILQ_FIRST(&clp->nfsc_nmp->nm_sess), cred, p); 2486191783Srmacklem if (error == NFSERR_CBPATHDOWN) 2487191783Srmacklem cbpathdown = 1; 2488244042Srmacklem else if (error == NFSERR_STALECLIENTID || 2489244042Srmacklem error == NFSERR_BADSESSION) { 2490206690Srmacklem NFSLOCKCLSTATE(); 2491191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECOVER; 2492206690Srmacklem NFSUNLOCKCLSTATE(); 2493206690Srmacklem } else if (error == NFSERR_EXPIRED) 2494191783Srmacklem (void) nfscl_hasexpired(clp, clidrev, p); 2495191783Srmacklem } 2496191783Srmacklem 2497244042Srmacklem /* Do renews for any DS sessions. */ 2498244042Srmacklemcheckdsrenew: 2499244042Srmacklem NFSLOCKMNT(clp->nfsc_nmp); 2500244042Srmacklem /* Skip first entry, since the MDS is handled above. */ 2501244042Srmacklem dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess); 2502244042Srmacklem if (dsp != NULL) 2503244042Srmacklem dsp = TAILQ_NEXT(dsp, nfsclds_list); 2504244042Srmacklem while (dsp != NULL) { 2505244042Srmacklem if (dsp->nfsclds_expire <= NFSD_MONOSEC) { 2506244042Srmacklem dsp->nfsclds_expire = NFSD_MONOSEC + 2507244042Srmacklem clp->nfsc_renew; 2508244042Srmacklem NFSUNLOCKMNT(clp->nfsc_nmp); 2509244042Srmacklem (void)nfsrpc_renew(clp, dsp, cred, p); 2510244042Srmacklem goto checkdsrenew; 2511244042Srmacklem } 2512244042Srmacklem dsp = TAILQ_NEXT(dsp, nfsclds_list); 2513244042Srmacklem } 2514244042Srmacklem NFSUNLOCKMNT(clp->nfsc_nmp); 2515244042Srmacklem 2516191783Srmacklem TAILQ_INIT(&dh); 2517191783Srmacklem NFSLOCKCLSTATE(); 2518191783Srmacklem if (cbpathdown) 2519191783Srmacklem /* It's a Total Recall! */ 2520191783Srmacklem nfscl_totalrecall(clp); 2521191783Srmacklem 2522191783Srmacklem /* 2523191783Srmacklem * Now, handle defunct owners. 2524191783Srmacklem */ 2525228217Srmacklem LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) { 2526228217Srmacklem if (LIST_EMPTY(&owp->nfsow_open)) { 2527228217Srmacklem if (owp->nfsow_defunct != 0) 2528228217Srmacklem nfscl_freeopenowner(owp, 0); 2529191783Srmacklem } 2530191783Srmacklem } 2531191783Srmacklem 2532191783Srmacklem /* 2533191783Srmacklem * Do the recall on any delegations. To avoid trouble, always 2534191783Srmacklem * come back up here after having slept. 2535191783Srmacklem */ 2536191783Srmacklem igotlock = 0; 2537191783Srmacklemtryagain: 2538191783Srmacklem dp = TAILQ_FIRST(&clp->nfsc_deleg); 2539191783Srmacklem while (dp != NULL) { 2540191783Srmacklem ndp = TAILQ_NEXT(dp, nfsdl_list); 2541191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_RECALL)) { 2542191783Srmacklem /* 2543191783Srmacklem * Wait for outstanding I/O ops to be done. 2544191783Srmacklem */ 2545191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 2546191783Srmacklem if (igotlock) { 2547191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2548191783Srmacklem igotlock = 0; 2549191783Srmacklem } 2550191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= 2551191783Srmacklem NFSV4LOCK_WANTED; 2552191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 2553191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", 2554191783Srmacklem NULL); 2555191783Srmacklem goto tryagain; 2556191783Srmacklem } 2557191783Srmacklem while (!igotlock) { 2558191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 2559222389Srmacklem &islept, NFSCLSTATEMUTEXPTR, NULL); 2560191783Srmacklem if (islept) 2561191783Srmacklem goto tryagain; 2562191783Srmacklem } 2563191783Srmacklem NFSUNLOCKCLSTATE(); 2564191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 2565191783Srmacklem ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp, 2566207082Srmacklem NULL, cred, p, 1); 2567191783Srmacklem if (!ret) { 2568191783Srmacklem nfscl_cleandeleg(dp); 2569191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, 2570191783Srmacklem nfsdl_list); 2571191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 2572191783Srmacklem TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list); 2573191783Srmacklem nfscl_delegcnt--; 2574191783Srmacklem newnfsstats.cldelegates--; 2575191783Srmacklem } 2576191783Srmacklem NFSLOCKCLSTATE(); 2577191783Srmacklem } 2578191783Srmacklem dp = ndp; 2579191783Srmacklem } 2580191783Srmacklem 2581191783Srmacklem /* 2582191783Srmacklem * Clear out old delegations, if we are above the high water 2583191783Srmacklem * mark. Only clear out ones with no state related to them. 2584191783Srmacklem * The tailq list is in LRU order. 2585191783Srmacklem */ 2586191783Srmacklem dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead); 2587191783Srmacklem while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) { 2588191783Srmacklem ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list); 2589191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt == 0 && 2590191783Srmacklem dp->nfsdl_rwlock.nfslock_lock == 0 && 2591191783Srmacklem dp->nfsdl_timestamp < NFSD_MONOSEC && 2592214406Srmacklem (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED | 2593214406Srmacklem NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) { 2594191783Srmacklem clearok = 1; 2595191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 2596191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 2597195819Srmacklem if (op != NULL) { 2598191783Srmacklem clearok = 0; 2599191783Srmacklem break; 2600191783Srmacklem } 2601191783Srmacklem } 2602191783Srmacklem if (clearok) { 2603191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 2604191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 2605191783Srmacklem clearok = 0; 2606191783Srmacklem break; 2607191783Srmacklem } 2608191783Srmacklem } 2609191783Srmacklem } 2610191783Srmacklem if (clearok) { 2611191783Srmacklem TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); 2612191783Srmacklem LIST_REMOVE(dp, nfsdl_hash); 2613191783Srmacklem TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list); 2614191783Srmacklem nfscl_delegcnt--; 2615191783Srmacklem newnfsstats.cldelegates--; 2616191783Srmacklem } 2617191783Srmacklem } 2618191783Srmacklem dp = ndp; 2619191783Srmacklem } 2620191783Srmacklem if (igotlock) 2621191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 2622244042Srmacklem 2623244042Srmacklem /* 2624244042Srmacklem * Do the recall on any layouts. To avoid trouble, always 2625244042Srmacklem * come back up here after having slept. 2626244042Srmacklem */ 2627244042Srmacklem TAILQ_INIT(&rlh); 2628244042Srmacklemtryagain2: 2629244042Srmacklem TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) { 2630244042Srmacklem if ((lyp->nfsly_flags & NFSLY_RECALL) != 0) { 2631244042Srmacklem /* 2632244042Srmacklem * Wait for outstanding I/O ops to be done. 2633244042Srmacklem */ 2634244042Srmacklem if (lyp->nfsly_lock.nfslock_usecnt > 0 || 2635244042Srmacklem (lyp->nfsly_lock.nfslock_lock & 2636244042Srmacklem NFSV4LOCK_LOCK) != 0) { 2637244042Srmacklem lyp->nfsly_lock.nfslock_lock |= 2638244042Srmacklem NFSV4LOCK_WANTED; 2639244042Srmacklem (void)nfsmsleep(&lyp->nfsly_lock, 2640244042Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfslyp", 2641244042Srmacklem NULL); 2642244042Srmacklem goto tryagain2; 2643244042Srmacklem } 2644244042Srmacklem /* Move the layout to the recall list. */ 2645244042Srmacklem TAILQ_REMOVE(&clp->nfsc_layout, lyp, 2646244042Srmacklem nfsly_list); 2647244042Srmacklem LIST_REMOVE(lyp, nfsly_hash); 2648244042Srmacklem TAILQ_INSERT_HEAD(&rlh, lyp, nfsly_list); 2649244042Srmacklem 2650244042Srmacklem /* Handle any layout commits. */ 2651244042Srmacklem if (!NFSHASNOLAYOUTCOMMIT(clp->nfsc_nmp) && 2652244042Srmacklem (lyp->nfsly_flags & NFSLY_WRITTEN) != 0) { 2653244042Srmacklem lyp->nfsly_flags &= ~NFSLY_WRITTEN; 2654244042Srmacklem NFSUNLOCKCLSTATE(); 2655244042Srmacklem NFSCL_DEBUG(3, "do layoutcommit\n"); 2656244042Srmacklem nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, 2657244042Srmacklem cred, p); 2658244042Srmacklem NFSLOCKCLSTATE(); 2659244042Srmacklem goto tryagain2; 2660244042Srmacklem } 2661244042Srmacklem } 2662244042Srmacklem } 2663244042Srmacklem 2664244042Srmacklem /* Now, look for stale layouts. */ 2665244042Srmacklem lyp = TAILQ_LAST(&clp->nfsc_layout, nfscllayouthead); 2666244042Srmacklem while (lyp != NULL) { 2667244042Srmacklem nlyp = TAILQ_PREV(lyp, nfscllayouthead, nfsly_list); 2668244042Srmacklem if (lyp->nfsly_timestamp < NFSD_MONOSEC && 2669244042Srmacklem (lyp->nfsly_flags & NFSLY_RECALL) == 0 && 2670244042Srmacklem lyp->nfsly_lock.nfslock_usecnt == 0 && 2671244042Srmacklem lyp->nfsly_lock.nfslock_lock == 0) { 2672244042Srmacklem NFSCL_DEBUG(4, "ret stale lay=%d\n", 2673244042Srmacklem nfscl_layoutcnt); 2674244042Srmacklem recallp = malloc(sizeof(*recallp), 2675244042Srmacklem M_NFSLAYRECALL, M_NOWAIT); 2676244042Srmacklem if (recallp == NULL) 2677244042Srmacklem break; 2678244042Srmacklem (void)nfscl_layoutrecall(NFSLAYOUTRETURN_FILE, 2679244042Srmacklem lyp, NFSLAYOUTIOMODE_ANY, 0, UINT64_MAX, 2680244042Srmacklem lyp->nfsly_stateid.seqid, recallp); 2681244042Srmacklem } 2682244042Srmacklem lyp = nlyp; 2683244042Srmacklem } 2684244042Srmacklem 2685244042Srmacklem /* 2686244042Srmacklem * Free up any unreferenced device info structures. 2687244042Srmacklem */ 2688244042Srmacklem LIST_FOREACH_SAFE(dip, &clp->nfsc_devinfo, nfsdi_list, ndip) { 2689244042Srmacklem if (dip->nfsdi_layoutrefs == 0 && 2690244042Srmacklem dip->nfsdi_refcnt == 0) { 2691244042Srmacklem NFSCL_DEBUG(4, "freeing devinfo\n"); 2692244042Srmacklem LIST_REMOVE(dip, nfsdi_list); 2693244042Srmacklem nfscl_freedevinfo(dip); 2694244042Srmacklem } 2695244042Srmacklem } 2696191783Srmacklem NFSUNLOCKCLSTATE(); 2697191783Srmacklem 2698244042Srmacklem /* Do layout return(s), as required. */ 2699244042Srmacklem TAILQ_FOREACH_SAFE(lyp, &rlh, nfsly_list, nlyp) { 2700244042Srmacklem TAILQ_REMOVE(&rlh, lyp, nfsly_list); 2701244042Srmacklem NFSCL_DEBUG(4, "ret layout\n"); 2702244042Srmacklem nfscl_layoutreturn(clp->nfsc_nmp, lyp, cred, p); 2703244042Srmacklem nfscl_freelayout(lyp); 2704244042Srmacklem } 2705244042Srmacklem 2706191783Srmacklem /* 2707191783Srmacklem * Delegreturn any delegations cleaned out or recalled. 2708191783Srmacklem */ 2709191783Srmacklem TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) { 2710191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 2711191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); 2712191783Srmacklem TAILQ_REMOVE(&dh, dp, nfsdl_list); 2713191783Srmacklem FREE((caddr_t)dp, M_NFSCLDELEG); 2714191783Srmacklem } 2715191783Srmacklem 2716228217Srmacklem SLIST_INIT(&lfh); 2717191783Srmacklem /* 2718227744Srmacklem * Call nfscl_cleanupkext() once per second to check for 2719227744Srmacklem * open/lock owners where the process has exited. 2720191783Srmacklem */ 2721245909Sjhb mytime = NFSD_MONOSEC; 2722245909Sjhb if (prevsec != mytime) { 2723245909Sjhb prevsec = mytime; 2724228217Srmacklem nfscl_cleanupkext(clp, &lfh); 2725191783Srmacklem } 2726191783Srmacklem 2727228217Srmacklem /* 2728228217Srmacklem * Do a ReleaseLockOwner for all lock owners where the 2729228217Srmacklem * associated process no longer exists, as found by 2730228217Srmacklem * nfscl_cleanupkext(). 2731228217Srmacklem */ 2732228217Srmacklem newnfs_setroot(cred); 2733228217Srmacklem SLIST_FOREACH_SAFE(lfhp, &lfh, nfslfh_list, nlfhp) { 2734228217Srmacklem LIST_FOREACH_SAFE(lp, &lfhp->nfslfh_lock, nfsl_list, 2735228217Srmacklem nlp) { 2736228217Srmacklem (void)nfsrpc_rellockown(clp->nfsc_nmp, lp, 2737228217Srmacklem lfhp->nfslfh_fh, lfhp->nfslfh_len, cred, 2738228217Srmacklem p); 2739228217Srmacklem nfscl_freelockowner(lp, 0); 2740228217Srmacklem } 2741228217Srmacklem free(lfhp, M_TEMP); 2742228217Srmacklem } 2743228217Srmacklem SLIST_INIT(&lfh); 2744228217Srmacklem 2745222389Srmacklem NFSLOCKCLSTATE(); 2746191783Srmacklem if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0) 2747222389Srmacklem (void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl", 2748222389Srmacklem hz); 2749191783Srmacklem if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) { 2750222389Srmacklem clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD; 2751222389Srmacklem NFSUNLOCKCLSTATE(); 2752191783Srmacklem NFSFREECRED(cred); 2753191783Srmacklem wakeup((caddr_t)clp); 2754191783Srmacklem return; 2755191783Srmacklem } 2756222389Srmacklem NFSUNLOCKCLSTATE(); 2757191783Srmacklem } 2758191783Srmacklem} 2759191783Srmacklem 2760191783Srmacklem/* 2761244042Srmacklem * Initiate state recovery. Called when NFSERR_STALECLIENTID, 2762244042Srmacklem * NFSERR_STALESTATEID or NFSERR_BADSESSION is received. 2763191783Srmacklem */ 2764191783SrmacklemAPPLESTATIC void 2765191783Srmacklemnfscl_initiate_recovery(struct nfsclclient *clp) 2766191783Srmacklem{ 2767191783Srmacklem 2768191783Srmacklem if (clp == NULL) 2769191783Srmacklem return; 2770191783Srmacklem NFSLOCKCLSTATE(); 2771191783Srmacklem clp->nfsc_flags |= NFSCLFLAGS_RECOVER; 2772191783Srmacklem NFSUNLOCKCLSTATE(); 2773191783Srmacklem wakeup((caddr_t)clp); 2774191783Srmacklem} 2775191783Srmacklem 2776191783Srmacklem/* 2777191783Srmacklem * Dump out the state stuff for debugging. 2778191783Srmacklem */ 2779191783SrmacklemAPPLESTATIC void 2780191783Srmacklemnfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens, 2781191783Srmacklem int lockowner, int locks) 2782191783Srmacklem{ 2783191783Srmacklem struct nfsclclient *clp; 2784191783Srmacklem struct nfsclowner *owp; 2785191783Srmacklem struct nfsclopen *op; 2786191783Srmacklem struct nfscllockowner *lp; 2787191783Srmacklem struct nfscllock *lop; 2788191783Srmacklem struct nfscldeleg *dp; 2789191783Srmacklem 2790191783Srmacklem clp = nmp->nm_clp; 2791191783Srmacklem if (clp == NULL) { 2792191783Srmacklem printf("nfscl dumpstate NULL clp\n"); 2793191783Srmacklem return; 2794191783Srmacklem } 2795191783Srmacklem NFSLOCKCLSTATE(); 2796191783Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 2797191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 2798191783Srmacklem if (openowner && !LIST_EMPTY(&owp->nfsow_open)) 2799191783Srmacklem printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n", 2800191783Srmacklem owp->nfsow_owner[0], owp->nfsow_owner[1], 2801191783Srmacklem owp->nfsow_owner[2], owp->nfsow_owner[3], 2802191783Srmacklem owp->nfsow_seqid); 2803191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2804191783Srmacklem if (opens) 2805191783Srmacklem printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n", 2806191783Srmacklem op->nfso_stateid.other[0], op->nfso_stateid.other[1], 2807191783Srmacklem op->nfso_stateid.other[2], op->nfso_opencnt, 2808191783Srmacklem op->nfso_fh[12]); 2809191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 2810191783Srmacklem if (lockowner) 2811191783Srmacklem printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n", 2812191783Srmacklem lp->nfsl_owner[0], lp->nfsl_owner[1], 2813191783Srmacklem lp->nfsl_owner[2], lp->nfsl_owner[3], 2814191783Srmacklem lp->nfsl_seqid, 2815191783Srmacklem lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1], 2816191783Srmacklem lp->nfsl_stateid.other[2]); 2817191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 2818191783Srmacklem if (locks) 2819191783Srmacklem#ifdef __FreeBSD__ 2820191783Srmacklem printf("lck typ=%d fst=%ju end=%ju\n", 2821191783Srmacklem lop->nfslo_type, (intmax_t)lop->nfslo_first, 2822191783Srmacklem (intmax_t)lop->nfslo_end); 2823191783Srmacklem#else 2824191783Srmacklem printf("lck typ=%d fst=%qd end=%qd\n", 2825191783Srmacklem lop->nfslo_type, lop->nfslo_first, 2826191783Srmacklem lop->nfslo_end); 2827191783Srmacklem#endif 2828191783Srmacklem } 2829191783Srmacklem } 2830191783Srmacklem } 2831191783Srmacklem } 2832191783Srmacklem } 2833191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2834191783Srmacklem if (openowner && !LIST_EMPTY(&owp->nfsow_open)) 2835191783Srmacklem printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n", 2836191783Srmacklem owp->nfsow_owner[0], owp->nfsow_owner[1], 2837191783Srmacklem owp->nfsow_owner[2], owp->nfsow_owner[3], 2838191783Srmacklem owp->nfsow_seqid); 2839191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2840191783Srmacklem if (opens) 2841191783Srmacklem printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n", 2842191783Srmacklem op->nfso_stateid.other[0], op->nfso_stateid.other[1], 2843191783Srmacklem op->nfso_stateid.other[2], op->nfso_opencnt, 2844191783Srmacklem op->nfso_fh[12]); 2845191783Srmacklem LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) { 2846191783Srmacklem if (lockowner) 2847191783Srmacklem printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n", 2848191783Srmacklem lp->nfsl_owner[0], lp->nfsl_owner[1], 2849191783Srmacklem lp->nfsl_owner[2], lp->nfsl_owner[3], 2850191783Srmacklem lp->nfsl_seqid, 2851191783Srmacklem lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1], 2852191783Srmacklem lp->nfsl_stateid.other[2]); 2853191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 2854191783Srmacklem if (locks) 2855191783Srmacklem#ifdef __FreeBSD__ 2856191783Srmacklem printf("lck typ=%d fst=%ju end=%ju\n", 2857191783Srmacklem lop->nfslo_type, (intmax_t)lop->nfslo_first, 2858191783Srmacklem (intmax_t)lop->nfslo_end); 2859191783Srmacklem#else 2860191783Srmacklem printf("lck typ=%d fst=%qd end=%qd\n", 2861191783Srmacklem lop->nfslo_type, lop->nfslo_first, 2862191783Srmacklem lop->nfslo_end); 2863191783Srmacklem#endif 2864191783Srmacklem } 2865191783Srmacklem } 2866191783Srmacklem } 2867191783Srmacklem } 2868191783Srmacklem NFSUNLOCKCLSTATE(); 2869191783Srmacklem} 2870191783Srmacklem 2871191783Srmacklem/* 2872191783Srmacklem * Check for duplicate open owners and opens. 2873191783Srmacklem * (Only used as a diagnostic aid.) 2874191783Srmacklem */ 2875191783SrmacklemAPPLESTATIC void 2876191783Srmacklemnfscl_dupopen(vnode_t vp, int dupopens) 2877191783Srmacklem{ 2878191783Srmacklem struct nfsclclient *clp; 2879191783Srmacklem struct nfsclowner *owp, *owp2; 2880191783Srmacklem struct nfsclopen *op, *op2; 2881191783Srmacklem struct nfsfh *nfhp; 2882191783Srmacklem 2883191783Srmacklem clp = VFSTONFS(vnode_mount(vp))->nm_clp; 2884191783Srmacklem if (clp == NULL) { 2885191783Srmacklem printf("nfscl dupopen NULL clp\n"); 2886191783Srmacklem return; 2887191783Srmacklem } 2888191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 2889191783Srmacklem NFSLOCKCLSTATE(); 2890191783Srmacklem 2891191783Srmacklem /* 2892191783Srmacklem * First, search for duplicate owners. 2893191783Srmacklem * These should never happen! 2894191783Srmacklem */ 2895191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2896191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2897191783Srmacklem if (owp != owp2 && 2898191783Srmacklem !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner, 2899191783Srmacklem NFSV4CL_LOCKNAMELEN)) { 2900191783Srmacklem NFSUNLOCKCLSTATE(); 2901191783Srmacklem printf("DUP OWNER\n"); 2902191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0); 2903191783Srmacklem return; 2904191783Srmacklem } 2905191783Srmacklem } 2906191783Srmacklem } 2907191783Srmacklem 2908191783Srmacklem /* 2909191783Srmacklem * Now, search for duplicate stateids. 2910191783Srmacklem * These shouldn't happen, either. 2911191783Srmacklem */ 2912191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2913191783Srmacklem LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) { 2914191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2915191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2916191783Srmacklem if (op != op2 && 2917191783Srmacklem (op->nfso_stateid.other[0] != 0 || 2918191783Srmacklem op->nfso_stateid.other[1] != 0 || 2919191783Srmacklem op->nfso_stateid.other[2] != 0) && 2920191783Srmacklem op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] && 2921191783Srmacklem op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] && 2922191783Srmacklem op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) { 2923191783Srmacklem NFSUNLOCKCLSTATE(); 2924191783Srmacklem printf("DUP STATEID\n"); 2925191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 2926191783Srmacklem 0); 2927191783Srmacklem return; 2928191783Srmacklem } 2929191783Srmacklem } 2930191783Srmacklem } 2931191783Srmacklem } 2932191783Srmacklem } 2933191783Srmacklem 2934191783Srmacklem /* 2935191783Srmacklem * Now search for duplicate opens. 2936191783Srmacklem * Duplicate opens for the same owner 2937191783Srmacklem * should never occur. Other duplicates are 2938191783Srmacklem * possible and are checked for if "dupopens" 2939191783Srmacklem * is true. 2940191783Srmacklem */ 2941191783Srmacklem LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) { 2942191783Srmacklem LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) { 2943191783Srmacklem if (nfhp->nfh_len == op2->nfso_fhlen && 2944191783Srmacklem !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) { 2945191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 2946191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 2947191783Srmacklem if (op != op2 && nfhp->nfh_len == op->nfso_fhlen && 2948191783Srmacklem !NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) && 2949191783Srmacklem (!NFSBCMP(op->nfso_own->nfsow_owner, 2950191783Srmacklem op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) || 2951191783Srmacklem dupopens)) { 2952191783Srmacklem if (!NFSBCMP(op->nfso_own->nfsow_owner, 2953191783Srmacklem op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) { 2954191783Srmacklem NFSUNLOCKCLSTATE(); 2955191783Srmacklem printf("BADDUP OPEN\n"); 2956191783Srmacklem } else { 2957191783Srmacklem NFSUNLOCKCLSTATE(); 2958191783Srmacklem printf("DUP OPEN\n"); 2959191783Srmacklem } 2960191783Srmacklem nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 2961191783Srmacklem 0, 0); 2962191783Srmacklem return; 2963191783Srmacklem } 2964191783Srmacklem } 2965191783Srmacklem } 2966191783Srmacklem } 2967191783Srmacklem } 2968191783Srmacklem } 2969191783Srmacklem NFSUNLOCKCLSTATE(); 2970191783Srmacklem} 2971191783Srmacklem 2972191783Srmacklem/* 2973191783Srmacklem * During close, find an open that needs to be dereferenced and 2974191783Srmacklem * dereference it. If there are no more opens for this file, 2975195510Srmacklem * log a message to that effect. 2976195510Srmacklem * Opens aren't actually Close'd until VOP_INACTIVE() is performed 2977195510Srmacklem * on the file's vnode. 2978191783Srmacklem * This is the safe way, since it is difficult to identify 2979195510Srmacklem * which open the close is for and I/O can be performed after the 2980195510Srmacklem * close(2) system call when a file is mmap'd. 2981191783Srmacklem * If it returns 0 for success, there will be a referenced 2982195510Srmacklem * clp returned via clpp. 2983191783Srmacklem */ 2984191783SrmacklemAPPLESTATIC int 2985195510Srmacklemnfscl_getclose(vnode_t vp, struct nfsclclient **clpp) 2986191783Srmacklem{ 2987191783Srmacklem struct nfsclclient *clp; 2988195510Srmacklem struct nfsclowner *owp; 2989195510Srmacklem struct nfsclopen *op; 2990191783Srmacklem struct nfscldeleg *dp; 2991191783Srmacklem struct nfsfh *nfhp; 2992195510Srmacklem int error, notdecr; 2993191783Srmacklem 2994244042Srmacklem error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp); 2995191783Srmacklem if (error) 2996191783Srmacklem return (error); 2997191783Srmacklem *clpp = clp; 2998191783Srmacklem 2999191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 3000191783Srmacklem notdecr = 1; 3001191783Srmacklem NFSLOCKCLSTATE(); 3002191783Srmacklem /* 3003191783Srmacklem * First, look for one under a delegation that was locally issued 3004191783Srmacklem * and just decrement the opencnt for it. Since all my Opens against 3005191783Srmacklem * the server are DENY_NONE, I don't see a problem with hanging 3006191783Srmacklem * onto them. (It is much easier to use one of the extant Opens 3007191783Srmacklem * that I already have on the server when a Delegation is recalled 3008195510Srmacklem * than to do fresh Opens.) Someday, I might need to rethink this, but. 3009191783Srmacklem */ 3010191783Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len); 3011191783Srmacklem if (dp != NULL) { 3012191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 3013191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 3014191783Srmacklem if (op != NULL) { 3015191783Srmacklem /* 3016191783Srmacklem * Since a delegation is for a file, there 3017191783Srmacklem * should never be more than one open for 3018191783Srmacklem * each openowner. 3019191783Srmacklem */ 3020191783Srmacklem if (LIST_NEXT(op, nfso_list) != NULL) 3021191783Srmacklem panic("nfscdeleg opens"); 3022191783Srmacklem if (notdecr && op->nfso_opencnt > 0) { 3023191783Srmacklem notdecr = 0; 3024191783Srmacklem op->nfso_opencnt--; 3025191783Srmacklem break; 3026191783Srmacklem } 3027191783Srmacklem } 3028191783Srmacklem } 3029191783Srmacklem } 3030191783Srmacklem 3031191783Srmacklem /* Now process the opens against the server. */ 3032191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 3033195510Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 3034192337Srmacklem if (op->nfso_fhlen == nfhp->nfh_len && 3035192337Srmacklem !NFSBCMP(op->nfso_fh, nfhp->nfh_fh, 3036192337Srmacklem nfhp->nfh_len)) { 3037192337Srmacklem /* Found an open, decrement cnt if possible */ 3038192337Srmacklem if (notdecr && op->nfso_opencnt > 0) { 3039192337Srmacklem notdecr = 0; 3040192337Srmacklem op->nfso_opencnt--; 3041192337Srmacklem } 3042192337Srmacklem /* 3043195510Srmacklem * There are more opens, so just return. 3044192337Srmacklem */ 3045192337Srmacklem if (op->nfso_opencnt > 0) { 3046192337Srmacklem NFSUNLOCKCLSTATE(); 3047192337Srmacklem return (0); 3048192337Srmacklem } 3049191783Srmacklem } 3050191783Srmacklem } 3051191783Srmacklem } 3052195510Srmacklem NFSUNLOCKCLSTATE(); 3053195510Srmacklem if (notdecr) 3054195510Srmacklem printf("nfscl: never fnd open\n"); 3055195510Srmacklem return (0); 3056195510Srmacklem} 3057191783Srmacklem 3058195510SrmacklemAPPLESTATIC int 3059195510Srmacklemnfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p) 3060195510Srmacklem{ 3061195510Srmacklem struct nfsclclient *clp; 3062195510Srmacklem struct nfsclowner *owp, *nowp; 3063195510Srmacklem struct nfsclopen *op; 3064195510Srmacklem struct nfscldeleg *dp; 3065195510Srmacklem struct nfsfh *nfhp; 3066195510Srmacklem int error; 3067195510Srmacklem 3068244042Srmacklem error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp); 3069195510Srmacklem if (error) 3070195510Srmacklem return (error); 3071195510Srmacklem *clpp = clp; 3072195510Srmacklem 3073195510Srmacklem nfhp = VTONFS(vp)->n_fhp; 3074195510Srmacklem NFSLOCKCLSTATE(); 3075195510Srmacklem /* 3076195510Srmacklem * First get rid of the local Open structures, which should be no 3077195510Srmacklem * longer in use. 3078195510Srmacklem */ 3079195510Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len); 3080195510Srmacklem if (dp != NULL) { 3081195510Srmacklem LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) { 3082191783Srmacklem op = LIST_FIRST(&owp->nfsow_open); 3083195510Srmacklem if (op != NULL) { 3084195510Srmacklem KASSERT((op->nfso_opencnt == 0), 3085195510Srmacklem ("nfscl: bad open cnt on deleg")); 3086195510Srmacklem nfscl_freeopen(op, 1); 3087191783Srmacklem } 3088195510Srmacklem nfscl_freeopenowner(owp, 1); 3089191783Srmacklem } 3090195510Srmacklem } 3091195510Srmacklem 3092244042Srmacklem /* Return any layouts marked return on close. */ 3093244042Srmacklem nfscl_retoncloselayout(clp, nfhp->nfh_fh, nfhp->nfh_len); 3094244042Srmacklem 3095195510Srmacklem /* Now process the opens against the server. */ 3096195510Srmacklemlookformore: 3097195510Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 3098195510Srmacklem op = LIST_FIRST(&owp->nfsow_open); 3099195510Srmacklem while (op != NULL) { 3100195510Srmacklem if (op->nfso_fhlen == nfhp->nfh_len && 3101195510Srmacklem !NFSBCMP(op->nfso_fh, nfhp->nfh_fh, 3102195510Srmacklem nfhp->nfh_len)) { 3103195510Srmacklem /* Found an open, close it. */ 3104195510Srmacklem KASSERT((op->nfso_opencnt == 0), 3105195510Srmacklem ("nfscl: bad open cnt on server")); 3106195510Srmacklem NFSUNLOCKCLSTATE(); 3107195510Srmacklem nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op, 3108195510Srmacklem p); 3109195510Srmacklem NFSLOCKCLSTATE(); 3110195510Srmacklem goto lookformore; 3111191783Srmacklem } 3112195510Srmacklem op = LIST_NEXT(op, nfso_list); 3113191783Srmacklem } 3114191783Srmacklem } 3115191783Srmacklem NFSUNLOCKCLSTATE(); 3116191783Srmacklem return (0); 3117191783Srmacklem} 3118191783Srmacklem 3119191783Srmacklem/* 3120191783Srmacklem * Return all delegations on this client. 3121191783Srmacklem * (Must be called with client sleep lock.) 3122191783Srmacklem */ 3123191783Srmacklemstatic void 3124191783Srmacklemnfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p) 3125191783Srmacklem{ 3126191783Srmacklem struct nfscldeleg *dp, *ndp; 3127191783Srmacklem struct ucred *cred; 3128191783Srmacklem 3129191783Srmacklem cred = newnfs_getcred(); 3130191783Srmacklem TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) { 3131191783Srmacklem nfscl_cleandeleg(dp); 3132191783Srmacklem (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); 3133191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 3134191783Srmacklem } 3135191783Srmacklem NFSFREECRED(cred); 3136191783Srmacklem} 3137191783Srmacklem 3138191783Srmacklem/* 3139191783Srmacklem * Do a callback RPC. 3140191783Srmacklem */ 3141191783SrmacklemAPPLESTATIC void 3142191783Srmacklemnfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) 3143191783Srmacklem{ 3144244042Srmacklem int clist, gotseq_ok, i, j, k, op, rcalls; 3145191783Srmacklem u_int32_t *tl; 3146191783Srmacklem struct nfsclclient *clp; 3147191783Srmacklem struct nfscldeleg *dp = NULL; 3148244042Srmacklem int numops, taglen = -1, error = 0, trunc; 3149191783Srmacklem u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident; 3150191783Srmacklem u_char tag[NFSV4_SMALLSTR + 1], *tagstr; 3151191783Srmacklem vnode_t vp = NULL; 3152191783Srmacklem struct nfsnode *np; 3153191783Srmacklem struct vattr va; 3154191783Srmacklem struct nfsfh *nfhp; 3155191783Srmacklem mount_t mp; 3156191783Srmacklem nfsattrbit_t attrbits, rattrbits; 3157191783Srmacklem nfsv4stateid_t stateid; 3158244042Srmacklem uint32_t seqid, slotid = 0, highslot, cachethis; 3159244042Srmacklem uint8_t sessionid[NFSX_V4SESSIONID]; 3160244042Srmacklem struct mbuf *rep; 3161244042Srmacklem struct nfscllayout *lyp; 3162244042Srmacklem uint64_t filesid[2], len, off; 3163244042Srmacklem int changed, gotone, laytype, recalltype; 3164244042Srmacklem uint32_t iomode; 3165244042Srmacklem struct nfsclrecalllayout *recallp = NULL; 3166191783Srmacklem 3167244042Srmacklem gotseq_ok = 0; 3168191783Srmacklem nfsrvd_rephead(nd); 3169191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 3170191783Srmacklem taglen = fxdr_unsigned(int, *tl); 3171191783Srmacklem if (taglen < 0) { 3172191783Srmacklem error = EBADRPC; 3173191783Srmacklem goto nfsmout; 3174191783Srmacklem } 3175191783Srmacklem if (taglen <= NFSV4_SMALLSTR) 3176191783Srmacklem tagstr = tag; 3177191783Srmacklem else 3178191783Srmacklem tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK); 3179191783Srmacklem error = nfsrv_mtostr(nd, tagstr, taglen); 3180191783Srmacklem if (error) { 3181191783Srmacklem if (taglen > NFSV4_SMALLSTR) 3182191783Srmacklem free(tagstr, M_TEMP); 3183191783Srmacklem taglen = -1; 3184191783Srmacklem goto nfsmout; 3185191783Srmacklem } 3186191783Srmacklem (void) nfsm_strtom(nd, tag, taglen); 3187191783Srmacklem if (taglen > NFSV4_SMALLSTR) { 3188191783Srmacklem free(tagstr, M_TEMP); 3189191783Srmacklem } 3190191783Srmacklem NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED); 3191191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 3192191783Srmacklem minorvers = fxdr_unsigned(u_int32_t, *tl++); 3193244042Srmacklem if (minorvers != NFSV4_MINORVERSION && minorvers != NFSV41_MINORVERSION) 3194191783Srmacklem nd->nd_repstat = NFSERR_MINORVERMISMATCH; 3195191783Srmacklem cbident = fxdr_unsigned(u_int32_t, *tl++); 3196191783Srmacklem if (nd->nd_repstat) 3197191783Srmacklem numops = 0; 3198191783Srmacklem else 3199191783Srmacklem numops = fxdr_unsigned(int, *tl); 3200191783Srmacklem /* 3201191783Srmacklem * Loop around doing the sub ops. 3202191783Srmacklem */ 3203191783Srmacklem for (i = 0; i < numops; i++) { 3204191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 3205191783Srmacklem NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED); 3206191783Srmacklem *repp++ = *tl; 3207191783Srmacklem op = fxdr_unsigned(int, *tl); 3208244042Srmacklem if (op < NFSV4OP_CBGETATTR || 3209244042Srmacklem (op > NFSV4OP_CBRECALL && minorvers == NFSV4_MINORVERSION) || 3210244042Srmacklem (op > NFSV4OP_CBNOTIFYDEVID && 3211244042Srmacklem minorvers == NFSV41_MINORVERSION)) { 3212191783Srmacklem nd->nd_repstat = NFSERR_OPILLEGAL; 3213191783Srmacklem *repp = nfscl_errmap(nd); 3214191783Srmacklem retops++; 3215191783Srmacklem break; 3216191783Srmacklem } 3217191783Srmacklem nd->nd_procnum = op; 3218244042Srmacklem if (op < NFSV4OP_CBNOPS) 3219244042Srmacklem newnfsstats.cbrpccnt[nd->nd_procnum]++; 3220191783Srmacklem switch (op) { 3221191783Srmacklem case NFSV4OP_CBGETATTR: 3222244042Srmacklem NFSCL_DEBUG(4, "cbgetattr\n"); 3223244042Srmacklem mp = NULL; 3224244042Srmacklem vp = NULL; 3225191783Srmacklem error = nfsm_getfh(nd, &nfhp); 3226191783Srmacklem if (!error) 3227191783Srmacklem error = nfsrv_getattrbits(nd, &attrbits, 3228191783Srmacklem NULL, NULL); 3229244042Srmacklem if (error == 0 && i == 0 && 3230244042Srmacklem minorvers != NFSV4_MINORVERSION) 3231244042Srmacklem error = NFSERR_OPNOTINSESS; 3232191783Srmacklem if (!error) { 3233244042Srmacklem mp = nfscl_getmnt(minorvers, sessionid, cbident, 3234244042Srmacklem &clp); 3235191783Srmacklem if (mp == NULL) 3236191783Srmacklem error = NFSERR_SERVERFAULT; 3237191783Srmacklem } 3238191783Srmacklem if (!error) { 3239244042Srmacklem error = nfscl_ngetreopen(mp, nfhp->nfh_fh, 3240244042Srmacklem nfhp->nfh_len, p, &np); 3241244042Srmacklem if (!error) 3242244042Srmacklem vp = NFSTOV(np); 3243244042Srmacklem } 3244244042Srmacklem if (!error) { 3245244042Srmacklem NFSZERO_ATTRBIT(&rattrbits); 3246191783Srmacklem NFSLOCKCLSTATE(); 3247244042Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, 3248244042Srmacklem nfhp->nfh_len); 3249244042Srmacklem if (dp != NULL) { 3250244042Srmacklem if (NFSISSET_ATTRBIT(&attrbits, 3251244042Srmacklem NFSATTRBIT_SIZE)) { 3252244042Srmacklem if (vp != NULL) 3253244042Srmacklem va.va_size = np->n_size; 3254244042Srmacklem else 3255244042Srmacklem va.va_size = 3256244042Srmacklem dp->nfsdl_size; 3257244042Srmacklem NFSSETBIT_ATTRBIT(&rattrbits, 3258244042Srmacklem NFSATTRBIT_SIZE); 3259244042Srmacklem } 3260244042Srmacklem if (NFSISSET_ATTRBIT(&attrbits, 3261244042Srmacklem NFSATTRBIT_CHANGE)) { 3262244042Srmacklem va.va_filerev = 3263244042Srmacklem dp->nfsdl_change; 3264244042Srmacklem if (vp == NULL || 3265244042Srmacklem (np->n_flag & NDELEGMOD)) 3266244042Srmacklem va.va_filerev++; 3267244042Srmacklem NFSSETBIT_ATTRBIT(&rattrbits, 3268244042Srmacklem NFSATTRBIT_CHANGE); 3269244042Srmacklem } 3270244042Srmacklem } else 3271244042Srmacklem error = NFSERR_SERVERFAULT; 3272191783Srmacklem NFSUNLOCKCLSTATE(); 3273191783Srmacklem } 3274244042Srmacklem if (vp != NULL) 3275244042Srmacklem vrele(vp); 3276244042Srmacklem if (mp != NULL) 3277244042Srmacklem vfs_unbusy(mp); 3278191783Srmacklem if (nfhp != NULL) 3279191783Srmacklem FREE((caddr_t)nfhp, M_NFSFH); 3280244042Srmacklem if (!error) 3281220645Srmacklem (void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va, 3282220648Srmacklem NULL, 0, &rattrbits, NULL, NULL, 0, 0, 0, 0, 3283220645Srmacklem (uint64_t)0); 3284191783Srmacklem break; 3285191783Srmacklem case NFSV4OP_CBRECALL: 3286244042Srmacklem NFSCL_DEBUG(4, "cbrecall\n"); 3287191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3288191783Srmacklem NFSX_UNSIGNED); 3289191783Srmacklem stateid.seqid = *tl++; 3290191783Srmacklem NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, 3291191783Srmacklem NFSX_STATEIDOTHER); 3292191783Srmacklem tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 3293191783Srmacklem trunc = fxdr_unsigned(int, *tl); 3294191783Srmacklem error = nfsm_getfh(nd, &nfhp); 3295244042Srmacklem if (error == 0 && i == 0 && 3296244042Srmacklem minorvers != NFSV4_MINORVERSION) 3297244042Srmacklem error = NFSERR_OPNOTINSESS; 3298191783Srmacklem if (!error) { 3299191783Srmacklem NFSLOCKCLSTATE(); 3300244042Srmacklem if (minorvers == NFSV4_MINORVERSION) 3301244042Srmacklem clp = nfscl_getclnt(cbident); 3302244042Srmacklem else 3303244042Srmacklem clp = nfscl_getclntsess(sessionid); 3304191783Srmacklem if (clp != NULL) { 3305191783Srmacklem dp = nfscl_finddeleg(clp, nfhp->nfh_fh, 3306191783Srmacklem nfhp->nfh_len); 3307214406Srmacklem if (dp != NULL && (dp->nfsdl_flags & 3308214406Srmacklem NFSCLDL_DELEGRET) == 0) { 3309191783Srmacklem dp->nfsdl_flags |= 3310191783Srmacklem NFSCLDL_RECALL; 3311191783Srmacklem wakeup((caddr_t)clp); 3312191783Srmacklem } 3313191783Srmacklem } else { 3314191783Srmacklem error = NFSERR_SERVERFAULT; 3315191783Srmacklem } 3316191783Srmacklem NFSUNLOCKCLSTATE(); 3317191783Srmacklem } 3318191783Srmacklem if (nfhp != NULL) 3319191783Srmacklem FREE((caddr_t)nfhp, M_NFSFH); 3320191783Srmacklem break; 3321244042Srmacklem case NFSV4OP_CBLAYOUTRECALL: 3322244042Srmacklem NFSCL_DEBUG(4, "cblayrec\n"); 3323244042Srmacklem nfhp = NULL; 3324244042Srmacklem NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED); 3325244042Srmacklem laytype = fxdr_unsigned(int, *tl++); 3326244042Srmacklem iomode = fxdr_unsigned(uint32_t, *tl++); 3327244042Srmacklem if (newnfs_true == *tl++) 3328244042Srmacklem changed = 1; 3329244042Srmacklem else 3330244042Srmacklem changed = 0; 3331244042Srmacklem recalltype = fxdr_unsigned(int, *tl); 3332244042Srmacklem recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL, 3333244042Srmacklem M_WAITOK); 3334244042Srmacklem if (laytype != NFSLAYOUT_NFSV4_1_FILES) 3335244042Srmacklem error = NFSERR_NOMATCHLAYOUT; 3336244042Srmacklem else if (recalltype == NFSLAYOUTRETURN_FILE) { 3337244042Srmacklem error = nfsm_getfh(nd, &nfhp); 3338244042Srmacklem NFSCL_DEBUG(4, "retfile getfh=%d\n", error); 3339244042Srmacklem if (error != 0) 3340244042Srmacklem goto nfsmout; 3341244042Srmacklem NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER + 3342244042Srmacklem NFSX_STATEID); 3343244042Srmacklem off = fxdr_hyper(tl); tl += 2; 3344244042Srmacklem len = fxdr_hyper(tl); tl += 2; 3345244042Srmacklem stateid.seqid = fxdr_unsigned(uint32_t, *tl++); 3346244042Srmacklem NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER); 3347244042Srmacklem if (minorvers == NFSV4_MINORVERSION) 3348244042Srmacklem error = NFSERR_NOTSUPP; 3349244042Srmacklem else if (i == 0) 3350244042Srmacklem error = NFSERR_OPNOTINSESS; 3351244042Srmacklem if (error == 0) { 3352244042Srmacklem NFSLOCKCLSTATE(); 3353244042Srmacklem clp = nfscl_getclntsess(sessionid); 3354244042Srmacklem NFSCL_DEBUG(4, "cbly clp=%p\n", clp); 3355244042Srmacklem if (clp != NULL) { 3356244042Srmacklem lyp = nfscl_findlayout(clp, 3357244042Srmacklem nfhp->nfh_fh, 3358244042Srmacklem nfhp->nfh_len); 3359244042Srmacklem NFSCL_DEBUG(4, "cblyp=%p\n", 3360244042Srmacklem lyp); 3361244042Srmacklem if (lyp != NULL && 3362244042Srmacklem (lyp->nfsly_flags & 3363244042Srmacklem NFSLY_FILES) != 0 && 3364244042Srmacklem !NFSBCMP(stateid.other, 3365244042Srmacklem lyp->nfsly_stateid.other, 3366244042Srmacklem NFSX_STATEIDOTHER)) { 3367244042Srmacklem error = 3368244042Srmacklem nfscl_layoutrecall( 3369244042Srmacklem recalltype, 3370244042Srmacklem lyp, iomode, off, 3371244042Srmacklem len, stateid.seqid, 3372244042Srmacklem recallp); 3373244042Srmacklem recallp = NULL; 3374244042Srmacklem wakeup(clp); 3375244042Srmacklem NFSCL_DEBUG(4, 3376244042Srmacklem "aft layrcal=%d\n", 3377244042Srmacklem error); 3378244042Srmacklem } else 3379244042Srmacklem error = 3380244042Srmacklem NFSERR_NOMATCHLAYOUT; 3381244042Srmacklem } else 3382244042Srmacklem error = NFSERR_NOMATCHLAYOUT; 3383244042Srmacklem NFSUNLOCKCLSTATE(); 3384244042Srmacklem } 3385244042Srmacklem free(nfhp, M_NFSFH); 3386244042Srmacklem } else if (recalltype == NFSLAYOUTRETURN_FSID) { 3387244042Srmacklem NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER); 3388244042Srmacklem filesid[0] = fxdr_hyper(tl); tl += 2; 3389244042Srmacklem filesid[1] = fxdr_hyper(tl); tl += 2; 3390244042Srmacklem gotone = 0; 3391244042Srmacklem NFSLOCKCLSTATE(); 3392244042Srmacklem clp = nfscl_getclntsess(sessionid); 3393244042Srmacklem if (clp != NULL) { 3394244042Srmacklem TAILQ_FOREACH(lyp, &clp->nfsc_layout, 3395244042Srmacklem nfsly_list) { 3396244042Srmacklem if (lyp->nfsly_filesid[0] == 3397244042Srmacklem filesid[0] && 3398244042Srmacklem lyp->nfsly_filesid[1] == 3399244042Srmacklem filesid[1]) { 3400244042Srmacklem error = 3401244042Srmacklem nfscl_layoutrecall( 3402244042Srmacklem recalltype, 3403244042Srmacklem lyp, iomode, 0, 3404244042Srmacklem UINT64_MAX, 3405244042Srmacklem lyp->nfsly_stateid.seqid, 3406244042Srmacklem recallp); 3407244042Srmacklem recallp = NULL; 3408244042Srmacklem gotone = 1; 3409244042Srmacklem } 3410244042Srmacklem } 3411244042Srmacklem if (gotone != 0) 3412244042Srmacklem wakeup(clp); 3413244042Srmacklem else 3414244042Srmacklem error = NFSERR_NOMATCHLAYOUT; 3415244042Srmacklem } else 3416244042Srmacklem error = NFSERR_NOMATCHLAYOUT; 3417244042Srmacklem NFSUNLOCKCLSTATE(); 3418244042Srmacklem } else if (recalltype == NFSLAYOUTRETURN_ALL) { 3419244042Srmacklem gotone = 0; 3420244042Srmacklem NFSLOCKCLSTATE(); 3421244042Srmacklem clp = nfscl_getclntsess(sessionid); 3422244042Srmacklem if (clp != NULL) { 3423244042Srmacklem TAILQ_FOREACH(lyp, &clp->nfsc_layout, 3424244042Srmacklem nfsly_list) { 3425244042Srmacklem error = nfscl_layoutrecall( 3426244042Srmacklem recalltype, lyp, iomode, 0, 3427244042Srmacklem UINT64_MAX, 3428244042Srmacklem lyp->nfsly_stateid.seqid, 3429244042Srmacklem recallp); 3430244042Srmacklem recallp = NULL; 3431244042Srmacklem gotone = 1; 3432244042Srmacklem } 3433244042Srmacklem if (gotone != 0) 3434244042Srmacklem wakeup(clp); 3435244042Srmacklem else 3436244042Srmacklem error = NFSERR_NOMATCHLAYOUT; 3437244042Srmacklem } else 3438244042Srmacklem error = NFSERR_NOMATCHLAYOUT; 3439244042Srmacklem NFSUNLOCKCLSTATE(); 3440244042Srmacklem } else 3441244042Srmacklem error = NFSERR_NOMATCHLAYOUT; 3442244042Srmacklem if (recallp != NULL) { 3443244042Srmacklem free(recallp, M_NFSLAYRECALL); 3444244042Srmacklem recallp = NULL; 3445244042Srmacklem } 3446244042Srmacklem break; 3447244042Srmacklem case NFSV4OP_CBSEQUENCE: 3448244042Srmacklem NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 3449244042Srmacklem 5 * NFSX_UNSIGNED); 3450244042Srmacklem bcopy(tl, sessionid, NFSX_V4SESSIONID); 3451244042Srmacklem tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; 3452244042Srmacklem seqid = fxdr_unsigned(uint32_t, *tl++); 3453244042Srmacklem slotid = fxdr_unsigned(uint32_t, *tl++); 3454244042Srmacklem highslot = fxdr_unsigned(uint32_t, *tl++); 3455244042Srmacklem cachethis = *tl++; 3456244042Srmacklem /* Throw away the referring call stuff. */ 3457244042Srmacklem clist = fxdr_unsigned(int, *tl); 3458244042Srmacklem for (j = 0; j < clist; j++) { 3459244042Srmacklem NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 3460244042Srmacklem NFSX_UNSIGNED); 3461244042Srmacklem tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; 3462244042Srmacklem rcalls = fxdr_unsigned(int, *tl); 3463244042Srmacklem for (k = 0; k < rcalls; k++) { 3464244042Srmacklem NFSM_DISSECT(tl, uint32_t *, 3465244042Srmacklem 2 * NFSX_UNSIGNED); 3466244042Srmacklem } 3467244042Srmacklem } 3468244042Srmacklem NFSLOCKCLSTATE(); 3469244042Srmacklem if (i == 0) { 3470244042Srmacklem clp = nfscl_getclntsess(sessionid); 3471244042Srmacklem if (clp == NULL) 3472244042Srmacklem error = NFSERR_SERVERFAULT; 3473244042Srmacklem } else 3474244042Srmacklem error = NFSERR_SEQUENCEPOS; 3475244042Srmacklem if (error == 0) 3476244042Srmacklem error = nfsv4_seqsession(seqid, slotid, 3477244042Srmacklem highslot, 3478244042Srmacklem NFSMNT_MDSSESSION(clp->nfsc_nmp)-> 3479244042Srmacklem nfsess_cbslots, &rep, 3480244042Srmacklem NFSMNT_MDSSESSION(clp->nfsc_nmp)-> 3481244042Srmacklem nfsess_backslots); 3482244042Srmacklem NFSUNLOCKCLSTATE(); 3483244042Srmacklem if (error == 0) { 3484244042Srmacklem gotseq_ok = 1; 3485244042Srmacklem if (rep != NULL) { 3486244042Srmacklem NFSCL_DEBUG(4, "Got cbretry\n"); 3487244042Srmacklem m_freem(nd->nd_mreq); 3488244042Srmacklem nd->nd_mreq = rep; 3489244042Srmacklem rep = NULL; 3490244042Srmacklem goto out; 3491244042Srmacklem } 3492244042Srmacklem NFSM_BUILD(tl, uint32_t *, 3493244042Srmacklem NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED); 3494244042Srmacklem bcopy(sessionid, tl, NFSX_V4SESSIONID); 3495244042Srmacklem tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; 3496244042Srmacklem *tl++ = txdr_unsigned(seqid); 3497244042Srmacklem *tl++ = txdr_unsigned(slotid); 3498244042Srmacklem *tl++ = txdr_unsigned(NFSV4_CBSLOTS - 1); 3499244042Srmacklem *tl = txdr_unsigned(NFSV4_CBSLOTS - 1); 3500244042Srmacklem } 3501244042Srmacklem break; 3502244042Srmacklem default: 3503244042Srmacklem if (i == 0 && minorvers == NFSV41_MINORVERSION) 3504244042Srmacklem error = NFSERR_OPNOTINSESS; 3505244042Srmacklem else { 3506244042Srmacklem NFSCL_DEBUG(1, "unsupp callback %d\n", op); 3507244042Srmacklem error = NFSERR_NOTSUPP; 3508244042Srmacklem } 3509244042Srmacklem break; 3510191783Srmacklem }; 3511191783Srmacklem if (error) { 3512191783Srmacklem if (error == EBADRPC || error == NFSERR_BADXDR) { 3513191783Srmacklem nd->nd_repstat = NFSERR_BADXDR; 3514191783Srmacklem } else { 3515191783Srmacklem nd->nd_repstat = error; 3516191783Srmacklem } 3517191783Srmacklem error = 0; 3518191783Srmacklem } 3519191783Srmacklem retops++; 3520191783Srmacklem if (nd->nd_repstat) { 3521191783Srmacklem *repp = nfscl_errmap(nd); 3522191783Srmacklem break; 3523191783Srmacklem } else 3524191783Srmacklem *repp = 0; /* NFS4_OK */ 3525191783Srmacklem } 3526191783Srmacklemnfsmout: 3527244042Srmacklem if (recallp != NULL) 3528244042Srmacklem free(recallp, M_NFSLAYRECALL); 3529191783Srmacklem if (error) { 3530191783Srmacklem if (error == EBADRPC || error == NFSERR_BADXDR) 3531191783Srmacklem nd->nd_repstat = NFSERR_BADXDR; 3532191783Srmacklem else 3533191783Srmacklem printf("nfsv4 comperr1=%d\n", error); 3534191783Srmacklem } 3535191783Srmacklem if (taglen == -1) { 3536191783Srmacklem NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 3537191783Srmacklem *tl++ = 0; 3538191783Srmacklem *tl = 0; 3539191783Srmacklem } else { 3540191783Srmacklem *retopsp = txdr_unsigned(retops); 3541191783Srmacklem } 3542191783Srmacklem *nd->nd_errp = nfscl_errmap(nd); 3543244042Srmacklemout: 3544244042Srmacklem if (gotseq_ok != 0) { 3545244042Srmacklem rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK); 3546244042Srmacklem NFSLOCKCLSTATE(); 3547244042Srmacklem clp = nfscl_getclntsess(sessionid); 3548244042Srmacklem if (clp != NULL) { 3549244042Srmacklem nfsv4_seqsess_cacherep(slotid, 3550244042Srmacklem NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_cbslots, 3551244042Srmacklem rep); 3552244042Srmacklem NFSUNLOCKCLSTATE(); 3553244042Srmacklem } else { 3554244042Srmacklem NFSUNLOCKCLSTATE(); 3555244042Srmacklem m_freem(rep); 3556244042Srmacklem } 3557244042Srmacklem } 3558191783Srmacklem} 3559191783Srmacklem 3560191783Srmacklem/* 3561191783Srmacklem * Generate the next cbident value. Basically just increment a static value 3562191783Srmacklem * and then check that it isn't already in the list, if it has wrapped around. 3563191783Srmacklem */ 3564191783Srmacklemstatic u_int32_t 3565191783Srmacklemnfscl_nextcbident(void) 3566191783Srmacklem{ 3567191783Srmacklem struct nfsclclient *clp; 3568191783Srmacklem int matched; 3569191783Srmacklem static u_int32_t nextcbident = 0; 3570191783Srmacklem static int haswrapped = 0; 3571191783Srmacklem 3572191783Srmacklem nextcbident++; 3573191783Srmacklem if (nextcbident == 0) 3574191783Srmacklem haswrapped = 1; 3575191783Srmacklem if (haswrapped) { 3576191783Srmacklem /* 3577191783Srmacklem * Search the clientid list for one already using this cbident. 3578191783Srmacklem */ 3579191783Srmacklem do { 3580191783Srmacklem matched = 0; 3581191783Srmacklem NFSLOCKCLSTATE(); 3582191783Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) { 3583191783Srmacklem if (clp->nfsc_cbident == nextcbident) { 3584191783Srmacklem matched = 1; 3585191783Srmacklem break; 3586191783Srmacklem } 3587191783Srmacklem } 3588191783Srmacklem NFSUNLOCKCLSTATE(); 3589191783Srmacklem if (matched == 1) 3590191783Srmacklem nextcbident++; 3591191783Srmacklem } while (matched); 3592191783Srmacklem } 3593191783Srmacklem return (nextcbident); 3594191783Srmacklem} 3595191783Srmacklem 3596191783Srmacklem/* 3597244042Srmacklem * Get the mount point related to a given cbident or session and busy it. 3598191783Srmacklem */ 3599191783Srmacklemstatic mount_t 3600244042Srmacklemnfscl_getmnt(int minorvers, uint8_t *sessionid, u_int32_t cbident, 3601244042Srmacklem struct nfsclclient **clpp) 3602191783Srmacklem{ 3603191783Srmacklem struct nfsclclient *clp; 3604244042Srmacklem mount_t mp; 3605244042Srmacklem int error; 3606191783Srmacklem 3607244042Srmacklem *clpp = NULL; 3608191783Srmacklem NFSLOCKCLSTATE(); 3609191783Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) { 3610244042Srmacklem if (minorvers == NFSV4_MINORVERSION) { 3611244042Srmacklem if (clp->nfsc_cbident == cbident) 3612244042Srmacklem break; 3613244042Srmacklem } else if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)-> 3614244042Srmacklem nfsess_sessionid, sessionid, NFSX_V4SESSIONID)) 3615191783Srmacklem break; 3616191783Srmacklem } 3617191783Srmacklem if (clp == NULL) { 3618191783Srmacklem NFSUNLOCKCLSTATE(); 3619191783Srmacklem return (NULL); 3620191783Srmacklem } 3621244042Srmacklem mp = clp->nfsc_nmp->nm_mountp; 3622244042Srmacklem vfs_ref(mp); 3623191783Srmacklem NFSUNLOCKCLSTATE(); 3624244042Srmacklem error = vfs_busy(mp, 0); 3625244042Srmacklem vfs_rel(mp); 3626244042Srmacklem if (error != 0) 3627244042Srmacklem return (NULL); 3628244042Srmacklem *clpp = clp; 3629244042Srmacklem return (mp); 3630191783Srmacklem} 3631191783Srmacklem 3632191783Srmacklem/* 3633244042Srmacklem * Get the clientid pointer related to a given cbident. 3634244042Srmacklem */ 3635244042Srmacklemstatic struct nfsclclient * 3636244042Srmacklemnfscl_getclnt(u_int32_t cbident) 3637244042Srmacklem{ 3638244042Srmacklem struct nfsclclient *clp; 3639244042Srmacklem 3640244042Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) 3641244042Srmacklem if (clp->nfsc_cbident == cbident) 3642244042Srmacklem break; 3643244042Srmacklem return (clp); 3644244042Srmacklem} 3645244042Srmacklem 3646244042Srmacklem/* 3647244042Srmacklem * Get the clientid pointer related to a given sessionid. 3648244042Srmacklem */ 3649244042Srmacklemstatic struct nfsclclient * 3650244042Srmacklemnfscl_getclntsess(uint8_t *sessionid) 3651244042Srmacklem{ 3652244042Srmacklem struct nfsclclient *clp; 3653244042Srmacklem 3654244042Srmacklem LIST_FOREACH(clp, &nfsclhead, nfsc_list) 3655244042Srmacklem if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_sessionid, 3656244042Srmacklem sessionid, NFSX_V4SESSIONID)) 3657244042Srmacklem break; 3658244042Srmacklem return (clp); 3659244042Srmacklem} 3660244042Srmacklem 3661244042Srmacklem/* 3662191783Srmacklem * Search for a lock conflict locally on the client. A conflict occurs if 3663191783Srmacklem * - not same owner and overlapping byte range and at least one of them is 3664191783Srmacklem * a write lock or this is an unlock. 3665191783Srmacklem */ 3666191783Srmacklemstatic int 3667201439Srmacklemnfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen, 3668201439Srmacklem struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp, 3669201439Srmacklem struct nfscllock **lopp) 3670191783Srmacklem{ 3671191783Srmacklem struct nfsclowner *owp; 3672191783Srmacklem struct nfsclopen *op; 3673191783Srmacklem int ret; 3674191783Srmacklem 3675191783Srmacklem if (dp != NULL) { 3676191783Srmacklem ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp); 3677191783Srmacklem if (ret) 3678191783Srmacklem return (ret); 3679191783Srmacklem } 3680191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 3681191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 3682201439Srmacklem if (op->nfso_fhlen == fhlen && 3683201439Srmacklem !NFSBCMP(op->nfso_fh, fhp, fhlen)) { 3684201439Srmacklem ret = nfscl_checkconflict(&op->nfso_lock, nlop, 3685201439Srmacklem own, lopp); 3686201439Srmacklem if (ret) 3687201439Srmacklem return (ret); 3688201439Srmacklem } 3689191783Srmacklem } 3690191783Srmacklem } 3691191783Srmacklem return (0); 3692191783Srmacklem} 3693191783Srmacklem 3694191783Srmacklemstatic int 3695191783Srmacklemnfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop, 3696191783Srmacklem u_int8_t *own, struct nfscllock **lopp) 3697191783Srmacklem{ 3698191783Srmacklem struct nfscllockowner *lp; 3699191783Srmacklem struct nfscllock *lop; 3700191783Srmacklem 3701191783Srmacklem LIST_FOREACH(lp, lhp, nfsl_list) { 3702191783Srmacklem if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) { 3703191783Srmacklem LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) { 3704191783Srmacklem if (lop->nfslo_first >= nlop->nfslo_end) 3705191783Srmacklem break; 3706191783Srmacklem if (lop->nfslo_end <= nlop->nfslo_first) 3707191783Srmacklem continue; 3708191783Srmacklem if (lop->nfslo_type == F_WRLCK || 3709191783Srmacklem nlop->nfslo_type == F_WRLCK || 3710191783Srmacklem nlop->nfslo_type == F_UNLCK) { 3711191783Srmacklem if (lopp != NULL) 3712191783Srmacklem *lopp = lop; 3713191783Srmacklem return (NFSERR_DENIED); 3714191783Srmacklem } 3715191783Srmacklem } 3716191783Srmacklem } 3717191783Srmacklem } 3718191783Srmacklem return (0); 3719191783Srmacklem} 3720191783Srmacklem 3721191783Srmacklem/* 3722191783Srmacklem * Check for a local conflicting lock. 3723191783Srmacklem */ 3724191783SrmacklemAPPLESTATIC int 3725191783Srmacklemnfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off, 3726222719Srmacklem u_int64_t len, struct flock *fl, NFSPROC_T *p, void *id, int flags) 3727191783Srmacklem{ 3728191783Srmacklem struct nfscllock *lop, nlck; 3729191783Srmacklem struct nfscldeleg *dp; 3730191783Srmacklem struct nfsnode *np; 3731191783Srmacklem u_int8_t own[NFSV4CL_LOCKNAMELEN]; 3732191783Srmacklem int error; 3733191783Srmacklem 3734191783Srmacklem nlck.nfslo_type = fl->l_type; 3735191783Srmacklem nlck.nfslo_first = off; 3736191783Srmacklem if (len == NFS64BITSSET) { 3737191783Srmacklem nlck.nfslo_end = NFS64BITSSET; 3738191783Srmacklem } else { 3739191783Srmacklem nlck.nfslo_end = off + len; 3740191783Srmacklem if (nlck.nfslo_end <= nlck.nfslo_first) 3741191783Srmacklem return (NFSERR_INVAL); 3742191783Srmacklem } 3743191783Srmacklem np = VTONFS(vp); 3744222719Srmacklem nfscl_filllockowner(id, own, flags); 3745191783Srmacklem NFSLOCKCLSTATE(); 3746191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 3747201439Srmacklem error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 3748201439Srmacklem &nlck, own, dp, &lop); 3749201439Srmacklem if (error != 0) { 3750191783Srmacklem fl->l_whence = SEEK_SET; 3751191783Srmacklem fl->l_start = lop->nfslo_first; 3752191783Srmacklem if (lop->nfslo_end == NFS64BITSSET) 3753191783Srmacklem fl->l_len = 0; 3754191783Srmacklem else 3755191783Srmacklem fl->l_len = lop->nfslo_end - lop->nfslo_first; 3756191783Srmacklem fl->l_pid = (pid_t)0; 3757191783Srmacklem fl->l_type = lop->nfslo_type; 3758201439Srmacklem error = -1; /* no RPC required */ 3759191783Srmacklem } else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) || 3760191783Srmacklem fl->l_type == F_RDLCK)) { 3761191783Srmacklem /* 3762191783Srmacklem * The delegation ensures that there isn't a conflicting 3763191783Srmacklem * lock on the server, so return -1 to indicate an RPC 3764191783Srmacklem * isn't required. 3765191783Srmacklem */ 3766191783Srmacklem fl->l_type = F_UNLCK; 3767191783Srmacklem error = -1; 3768191783Srmacklem } 3769191783Srmacklem NFSUNLOCKCLSTATE(); 3770191783Srmacklem return (error); 3771191783Srmacklem} 3772191783Srmacklem 3773191783Srmacklem/* 3774191783Srmacklem * Handle Recall of a delegation. 3775191783Srmacklem * The clp must be exclusive locked when this is called. 3776191783Srmacklem */ 3777191783Srmacklemstatic int 3778191783Srmacklemnfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp, 3779207082Srmacklem struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p, 3780207082Srmacklem int called_from_renewthread) 3781191783Srmacklem{ 3782191783Srmacklem struct nfsclowner *owp, *lowp, *nowp; 3783191783Srmacklem struct nfsclopen *op, *lop; 3784191783Srmacklem struct nfscllockowner *lp; 3785191783Srmacklem struct nfscllock *lckp; 3786191783Srmacklem struct nfsnode *np; 3787191783Srmacklem int error = 0, ret, gotvp = 0; 3788191783Srmacklem 3789191783Srmacklem if (vp == NULL) { 3790191783Srmacklem /* 3791191783Srmacklem * First, get a vnode for the file. This is needed to do RPCs. 3792191783Srmacklem */ 3793191783Srmacklem ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh, 3794191783Srmacklem dp->nfsdl_fhlen, p, &np); 3795191783Srmacklem if (ret) { 3796191783Srmacklem /* 3797191783Srmacklem * File isn't open, so nothing to move over to the 3798191783Srmacklem * server. 3799191783Srmacklem */ 3800191783Srmacklem return (0); 3801191783Srmacklem } 3802191783Srmacklem vp = NFSTOV(np); 3803191783Srmacklem gotvp = 1; 3804191783Srmacklem } else { 3805191783Srmacklem np = VTONFS(vp); 3806191783Srmacklem } 3807191783Srmacklem dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET; 3808191783Srmacklem 3809191783Srmacklem /* 3810191783Srmacklem * Ok, if it's a write delegation, flush data to the server, so 3811191783Srmacklem * that close/open consistency is retained. 3812191783Srmacklem */ 3813207082Srmacklem ret = 0; 3814191783Srmacklem NFSLOCKNODE(np); 3815191783Srmacklem if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) { 3816191783Srmacklem np->n_flag |= NDELEGRECALL; 3817191783Srmacklem NFSUNLOCKNODE(np); 3818207082Srmacklem ret = ncl_flush(vp, MNT_WAIT, cred, p, 1, 3819207082Srmacklem called_from_renewthread); 3820191783Srmacklem NFSLOCKNODE(np); 3821214406Srmacklem np->n_flag &= ~NDELEGRECALL; 3822191783Srmacklem } 3823214406Srmacklem NFSINVALATTRCACHE(np); 3824191783Srmacklem NFSUNLOCKNODE(np); 3825207082Srmacklem if (ret == EIO && called_from_renewthread != 0) { 3826207082Srmacklem /* 3827207082Srmacklem * If the flush failed with EIO for the renew thread, 3828207082Srmacklem * return now, so that the dirty buffer will be flushed 3829207082Srmacklem * later. 3830207082Srmacklem */ 3831207082Srmacklem if (gotvp != 0) 3832207082Srmacklem vrele(vp); 3833207082Srmacklem return (ret); 3834207082Srmacklem } 3835191783Srmacklem 3836191783Srmacklem /* 3837191783Srmacklem * Now, for each openowner with opens issued locally, move them 3838191783Srmacklem * over to state against the server. 3839191783Srmacklem */ 3840191783Srmacklem LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) { 3841191783Srmacklem lop = LIST_FIRST(&lowp->nfsow_open); 3842191783Srmacklem if (lop != NULL) { 3843191783Srmacklem if (LIST_NEXT(lop, nfso_list) != NULL) 3844191783Srmacklem panic("nfsdlg mult opens"); 3845191783Srmacklem /* 3846191783Srmacklem * Look for the same openowner against the server. 3847191783Srmacklem */ 3848191783Srmacklem LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { 3849191783Srmacklem if (!NFSBCMP(lowp->nfsow_owner, 3850191783Srmacklem owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) { 3851191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3852191783Srmacklem ret = nfscl_moveopen(vp, clp, nmp, lop, 3853191783Srmacklem owp, dp, cred, p); 3854191783Srmacklem if (ret == NFSERR_STALECLIENTID || 3855244042Srmacklem ret == NFSERR_STALEDONTRECOVER || 3856244042Srmacklem ret == NFSERR_BADSESSION) { 3857191783Srmacklem if (gotvp) 3858191783Srmacklem vrele(vp); 3859191783Srmacklem return (ret); 3860191783Srmacklem } 3861191783Srmacklem if (ret) { 3862191783Srmacklem nfscl_freeopen(lop, 1); 3863191783Srmacklem if (!error) 3864191783Srmacklem error = ret; 3865191783Srmacklem } 3866191783Srmacklem break; 3867191783Srmacklem } 3868191783Srmacklem } 3869191783Srmacklem 3870191783Srmacklem /* 3871191783Srmacklem * If no openowner found, create one and get an open 3872191783Srmacklem * for it. 3873191783Srmacklem */ 3874191783Srmacklem if (owp == NULL) { 3875191783Srmacklem MALLOC(nowp, struct nfsclowner *, 3876191783Srmacklem sizeof (struct nfsclowner), M_NFSCLOWNER, 3877191783Srmacklem M_WAITOK); 3878191783Srmacklem nfscl_newopen(clp, NULL, &owp, &nowp, &op, 3879191783Srmacklem NULL, lowp->nfsow_owner, dp->nfsdl_fh, 3880191783Srmacklem dp->nfsdl_fhlen, NULL); 3881191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3882191783Srmacklem ret = nfscl_moveopen(vp, clp, nmp, lop, 3883191783Srmacklem owp, dp, cred, p); 3884191783Srmacklem if (ret) { 3885191783Srmacklem nfscl_freeopenowner(owp, 0); 3886191783Srmacklem if (ret == NFSERR_STALECLIENTID || 3887244042Srmacklem ret == NFSERR_STALEDONTRECOVER || 3888244042Srmacklem ret == NFSERR_BADSESSION) { 3889191783Srmacklem if (gotvp) 3890191783Srmacklem vrele(vp); 3891191783Srmacklem return (ret); 3892191783Srmacklem } 3893191783Srmacklem if (ret) { 3894191783Srmacklem nfscl_freeopen(lop, 1); 3895191783Srmacklem if (!error) 3896191783Srmacklem error = ret; 3897191783Srmacklem } 3898191783Srmacklem } 3899191783Srmacklem } 3900191783Srmacklem } 3901191783Srmacklem } 3902191783Srmacklem 3903191783Srmacklem /* 3904191783Srmacklem * Now, get byte range locks for any locks done locally. 3905191783Srmacklem */ 3906191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 3907191783Srmacklem LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) { 3908191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 3909191783Srmacklem ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p); 3910191783Srmacklem if (ret == NFSERR_STALESTATEID || 3911191783Srmacklem ret == NFSERR_STALEDONTRECOVER || 3912244042Srmacklem ret == NFSERR_STALECLIENTID || 3913244042Srmacklem ret == NFSERR_BADSESSION) { 3914191783Srmacklem if (gotvp) 3915191783Srmacklem vrele(vp); 3916191783Srmacklem return (ret); 3917191783Srmacklem } 3918191783Srmacklem if (ret && !error) 3919191783Srmacklem error = ret; 3920191783Srmacklem } 3921191783Srmacklem } 3922191783Srmacklem if (gotvp) 3923191783Srmacklem vrele(vp); 3924191783Srmacklem return (error); 3925191783Srmacklem} 3926191783Srmacklem 3927191783Srmacklem/* 3928191783Srmacklem * Move a locally issued open over to an owner on the state list. 3929191783Srmacklem * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and 3930191783Srmacklem * returns with it unlocked. 3931191783Srmacklem */ 3932191783Srmacklemstatic int 3933191783Srmacklemnfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp, 3934191783Srmacklem struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp, 3935191783Srmacklem struct ucred *cred, NFSPROC_T *p) 3936191783Srmacklem{ 3937191783Srmacklem struct nfsclopen *op, *nop; 3938191783Srmacklem struct nfscldeleg *ndp; 3939191783Srmacklem struct nfsnode *np; 3940191783Srmacklem int error = 0, newone; 3941191783Srmacklem 3942191783Srmacklem /* 3943191783Srmacklem * First, look for an appropriate open, If found, just increment the 3944191783Srmacklem * opencnt in it. 3945191783Srmacklem */ 3946191783Srmacklem LIST_FOREACH(op, &owp->nfsow_open, nfso_list) { 3947191783Srmacklem if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode && 3948191783Srmacklem op->nfso_fhlen == lop->nfso_fhlen && 3949191783Srmacklem !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) { 3950191783Srmacklem op->nfso_opencnt += lop->nfso_opencnt; 3951191783Srmacklem nfscl_freeopen(lop, 1); 3952191783Srmacklem return (0); 3953191783Srmacklem } 3954191783Srmacklem } 3955191783Srmacklem 3956191783Srmacklem /* No appropriate open, so we have to do one against the server. */ 3957191783Srmacklem np = VTONFS(vp); 3958191783Srmacklem MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + 3959191783Srmacklem lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK); 3960191783Srmacklem newone = 0; 3961191783Srmacklem nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner, 3962191783Srmacklem lop->nfso_fh, lop->nfso_fhlen, &newone); 3963191783Srmacklem ndp = dp; 3964191783Srmacklem error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen, 3965191783Srmacklem lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op, 3966191783Srmacklem NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p); 3967191783Srmacklem if (error) { 3968191783Srmacklem if (newone) 3969191783Srmacklem nfscl_freeopen(op, 0); 3970191783Srmacklem } else { 3971191783Srmacklem if (newone) 3972191783Srmacklem newnfs_copyincred(cred, &op->nfso_cred); 3973191783Srmacklem op->nfso_mode |= lop->nfso_mode; 3974191783Srmacklem op->nfso_opencnt += lop->nfso_opencnt; 3975191783Srmacklem nfscl_freeopen(lop, 1); 3976191783Srmacklem } 3977191783Srmacklem if (nop != NULL) 3978191783Srmacklem FREE((caddr_t)nop, M_NFSCLOPEN); 3979191783Srmacklem if (ndp != NULL) { 3980191783Srmacklem /* 3981191783Srmacklem * What should I do with the returned delegation, since the 3982191783Srmacklem * delegation is being recalled? For now, just printf and 3983191783Srmacklem * through it away. 3984191783Srmacklem */ 3985191783Srmacklem printf("Moveopen returned deleg\n"); 3986191783Srmacklem FREE((caddr_t)ndp, M_NFSCLDELEG); 3987191783Srmacklem } 3988191783Srmacklem return (error); 3989191783Srmacklem} 3990191783Srmacklem 3991191783Srmacklem/* 3992191783Srmacklem * Recall all delegations on this client. 3993191783Srmacklem */ 3994191783Srmacklemstatic void 3995191783Srmacklemnfscl_totalrecall(struct nfsclclient *clp) 3996191783Srmacklem{ 3997191783Srmacklem struct nfscldeleg *dp; 3998191783Srmacklem 3999214406Srmacklem TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) { 4000214406Srmacklem if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0) 4001214406Srmacklem dp->nfsdl_flags |= NFSCLDL_RECALL; 4002214406Srmacklem } 4003191783Srmacklem} 4004191783Srmacklem 4005191783Srmacklem/* 4006191783Srmacklem * Relock byte ranges. Called for delegation recall and state expiry. 4007191783Srmacklem */ 4008191783Srmacklemstatic int 4009191783Srmacklemnfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp, 4010191783Srmacklem struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred, 4011191783Srmacklem NFSPROC_T *p) 4012191783Srmacklem{ 4013191783Srmacklem struct nfscllockowner *nlp; 4014191783Srmacklem struct nfsfh *nfhp; 4015191783Srmacklem u_int64_t off, len; 4016191783Srmacklem u_int32_t clidrev = 0; 4017191783Srmacklem int error, newone, donelocally; 4018191783Srmacklem 4019191783Srmacklem off = lop->nfslo_first; 4020191783Srmacklem len = lop->nfslo_end - lop->nfslo_first; 4021191783Srmacklem error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p, 4022228217Srmacklem clp, 1, NULL, lp->nfsl_lockflags, lp->nfsl_owner, 4023228217Srmacklem lp->nfsl_openowner, &nlp, &newone, &donelocally); 4024191783Srmacklem if (error || donelocally) 4025191783Srmacklem return (error); 4026191783Srmacklem if (nmp->nm_clp != NULL) 4027191783Srmacklem clidrev = nmp->nm_clp->nfsc_clientidrev; 4028191783Srmacklem else 4029191783Srmacklem clidrev = 0; 4030191783Srmacklem nfhp = VTONFS(vp)->n_fhp; 4031191783Srmacklem error = nfscl_trylock(nmp, vp, nfhp->nfh_fh, 4032191783Srmacklem nfhp->nfh_len, nlp, newone, 0, off, 4033191783Srmacklem len, lop->nfslo_type, cred, p); 4034191783Srmacklem if (error) 4035191783Srmacklem nfscl_freelockowner(nlp, 0); 4036191783Srmacklem return (error); 4037191783Srmacklem} 4038191783Srmacklem 4039191783Srmacklem/* 4040191783Srmacklem * Called to re-open a file. Basically get a vnode for the file handle 4041191783Srmacklem * and then call nfsrpc_openrpc() to do the rest. 4042191783Srmacklem */ 4043191783Srmacklemstatic int 4044191783Srmacklemnfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, 4045191783Srmacklem u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp, 4046191783Srmacklem struct ucred *cred, NFSPROC_T *p) 4047191783Srmacklem{ 4048191783Srmacklem struct nfsnode *np; 4049191783Srmacklem vnode_t vp; 4050191783Srmacklem int error; 4051191783Srmacklem 4052191783Srmacklem error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np); 4053191783Srmacklem if (error) 4054191783Srmacklem return (error); 4055191783Srmacklem vp = NFSTOV(np); 4056191783Srmacklem if (np->n_v4 != NULL) { 4057191783Srmacklem error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, 4058191783Srmacklem np->n_v4->n4_fhlen, fhp, fhlen, mode, op, 4059191783Srmacklem NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0, 4060191783Srmacklem cred, p); 4061191783Srmacklem } else { 4062191783Srmacklem error = EINVAL; 4063191783Srmacklem } 4064191783Srmacklem vrele(vp); 4065191783Srmacklem return (error); 4066191783Srmacklem} 4067191783Srmacklem 4068191783Srmacklem/* 4069191783Srmacklem * Try an open against the server. Just call nfsrpc_openrpc(), retrying while 4070191783Srmacklem * NFSERR_DELAY. Also, try system credentials, if the passed in credentials 4071191783Srmacklem * fail. 4072191783Srmacklem */ 4073191783Srmacklemstatic int 4074191783Srmacklemnfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen, 4075191783Srmacklem u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op, 4076191783Srmacklem u_int8_t *name, int namelen, struct nfscldeleg **ndpp, 4077191783Srmacklem int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p) 4078191783Srmacklem{ 4079191783Srmacklem int error; 4080191783Srmacklem 4081191783Srmacklem do { 4082191783Srmacklem error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen, 4083191783Srmacklem mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p, 4084191783Srmacklem 0, 0); 4085191783Srmacklem if (error == NFSERR_DELAY) 4086207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstryop"); 4087191783Srmacklem } while (error == NFSERR_DELAY); 4088191783Srmacklem if (error == EAUTH || error == EACCES) { 4089191783Srmacklem /* Try again using system credentials */ 4090191783Srmacklem newnfs_setroot(cred); 4091191783Srmacklem do { 4092191783Srmacklem error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, 4093191783Srmacklem newfhlen, mode, op, name, namelen, ndpp, reclaim, 4094191783Srmacklem delegtype, cred, p, 1, 0); 4095191783Srmacklem if (error == NFSERR_DELAY) 4096207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstryop"); 4097191783Srmacklem } while (error == NFSERR_DELAY); 4098191783Srmacklem } 4099191783Srmacklem return (error); 4100191783Srmacklem} 4101191783Srmacklem 4102191783Srmacklem/* 4103191783Srmacklem * Try a byte range lock. Just loop on nfsrpc_lock() while it returns 4104191783Srmacklem * NFSERR_DELAY. Also, retry with system credentials, if the provided 4105191783Srmacklem * cred don't work. 4106191783Srmacklem */ 4107191783Srmacklemstatic int 4108191783Srmacklemnfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, 4109191783Srmacklem int fhlen, struct nfscllockowner *nlp, int newone, int reclaim, 4110191783Srmacklem u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p) 4111191783Srmacklem{ 4112191783Srmacklem struct nfsrv_descript nfsd, *nd = &nfsd; 4113191783Srmacklem int error; 4114191783Srmacklem 4115191783Srmacklem do { 4116191783Srmacklem error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone, 4117191783Srmacklem reclaim, off, len, type, cred, p, 0); 4118191783Srmacklem if (!error && nd->nd_repstat == NFSERR_DELAY) 4119207170Srmacklem (void) nfs_catnap(PZERO, (int)nd->nd_repstat, 4120207170Srmacklem "nfstrylck"); 4121191783Srmacklem } while (!error && nd->nd_repstat == NFSERR_DELAY); 4122191783Srmacklem if (!error) 4123191783Srmacklem error = nd->nd_repstat; 4124191783Srmacklem if (error == EAUTH || error == EACCES) { 4125191783Srmacklem /* Try again using root credentials */ 4126191783Srmacklem newnfs_setroot(cred); 4127191783Srmacklem do { 4128191783Srmacklem error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, 4129191783Srmacklem newone, reclaim, off, len, type, cred, p, 1); 4130191783Srmacklem if (!error && nd->nd_repstat == NFSERR_DELAY) 4131207170Srmacklem (void) nfs_catnap(PZERO, (int)nd->nd_repstat, 4132207170Srmacklem "nfstrylck"); 4133191783Srmacklem } while (!error && nd->nd_repstat == NFSERR_DELAY); 4134191783Srmacklem if (!error) 4135191783Srmacklem error = nd->nd_repstat; 4136191783Srmacklem } 4137191783Srmacklem return (error); 4138191783Srmacklem} 4139191783Srmacklem 4140191783Srmacklem/* 4141191783Srmacklem * Try a delegreturn against the server. Just call nfsrpc_delegreturn(), 4142191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in 4143191783Srmacklem * credentials fail. 4144191783Srmacklem */ 4145191783Srmacklemstatic int 4146191783Srmacklemnfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred, 4147191783Srmacklem struct nfsmount *nmp, NFSPROC_T *p) 4148191783Srmacklem{ 4149191783Srmacklem int error; 4150191783Srmacklem 4151191783Srmacklem do { 4152191783Srmacklem error = nfsrpc_delegreturn(dp, cred, nmp, p, 0); 4153191783Srmacklem if (error == NFSERR_DELAY) 4154207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstrydp"); 4155191783Srmacklem } while (error == NFSERR_DELAY); 4156191783Srmacklem if (error == EAUTH || error == EACCES) { 4157191783Srmacklem /* Try again using system credentials */ 4158191783Srmacklem newnfs_setroot(cred); 4159191783Srmacklem do { 4160191783Srmacklem error = nfsrpc_delegreturn(dp, cred, nmp, p, 1); 4161191783Srmacklem if (error == NFSERR_DELAY) 4162207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstrydp"); 4163191783Srmacklem } while (error == NFSERR_DELAY); 4164191783Srmacklem } 4165191783Srmacklem return (error); 4166191783Srmacklem} 4167191783Srmacklem 4168191783Srmacklem/* 4169191783Srmacklem * Try a close against the server. Just call nfsrpc_closerpc(), 4170191783Srmacklem * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in 4171191783Srmacklem * credentials fail. 4172191783Srmacklem */ 4173191783SrmacklemAPPLESTATIC int 4174191783Srmacklemnfscl_tryclose(struct nfsclopen *op, struct ucred *cred, 4175191783Srmacklem struct nfsmount *nmp, NFSPROC_T *p) 4176191783Srmacklem{ 4177191783Srmacklem struct nfsrv_descript nfsd, *nd = &nfsd; 4178191783Srmacklem int error; 4179191783Srmacklem 4180191783Srmacklem do { 4181191783Srmacklem error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0); 4182191783Srmacklem if (error == NFSERR_DELAY) 4183207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstrycl"); 4184191783Srmacklem } while (error == NFSERR_DELAY); 4185191783Srmacklem if (error == EAUTH || error == EACCES) { 4186191783Srmacklem /* Try again using system credentials */ 4187191783Srmacklem newnfs_setroot(cred); 4188191783Srmacklem do { 4189191783Srmacklem error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1); 4190191783Srmacklem if (error == NFSERR_DELAY) 4191207170Srmacklem (void) nfs_catnap(PZERO, error, "nfstrycl"); 4192191783Srmacklem } while (error == NFSERR_DELAY); 4193191783Srmacklem } 4194191783Srmacklem return (error); 4195191783Srmacklem} 4196191783Srmacklem 4197191783Srmacklem/* 4198191783Srmacklem * Decide if a delegation on a file permits close without flushing writes 4199191783Srmacklem * to the server. This might be a big performance win in some environments. 4200191783Srmacklem * (Not useful until the client does caching on local stable storage.) 4201191783Srmacklem */ 4202210786SrmacklemAPPLESTATIC int 4203191783Srmacklemnfscl_mustflush(vnode_t vp) 4204191783Srmacklem{ 4205191783Srmacklem struct nfsclclient *clp; 4206191783Srmacklem struct nfscldeleg *dp; 4207191783Srmacklem struct nfsnode *np; 4208191783Srmacklem struct nfsmount *nmp; 4209191783Srmacklem 4210191783Srmacklem np = VTONFS(vp); 4211191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4212191783Srmacklem if (!NFSHASNFSV4(nmp)) 4213210786Srmacklem return (1); 4214191783Srmacklem NFSLOCKCLSTATE(); 4215191783Srmacklem clp = nfscl_findcl(nmp); 4216191783Srmacklem if (clp == NULL) { 4217191783Srmacklem NFSUNLOCKCLSTATE(); 4218210786Srmacklem return (1); 4219191783Srmacklem } 4220191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4221214406Srmacklem if (dp != NULL && (dp->nfsdl_flags & 4222214406Srmacklem (NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 4223214406Srmacklem NFSCLDL_WRITE && 4224191783Srmacklem (dp->nfsdl_sizelimit >= np->n_size || 4225191783Srmacklem !NFSHASSTRICT3530(nmp))) { 4226191783Srmacklem NFSUNLOCKCLSTATE(); 4227210786Srmacklem return (0); 4228191783Srmacklem } 4229191783Srmacklem NFSUNLOCKCLSTATE(); 4230210786Srmacklem return (1); 4231191783Srmacklem} 4232191783Srmacklem 4233191783Srmacklem/* 4234191783Srmacklem * See if a (write) delegation exists for this file. 4235191783Srmacklem */ 4236191783SrmacklemAPPLESTATIC int 4237191783Srmacklemnfscl_nodeleg(vnode_t vp, int writedeleg) 4238191783Srmacklem{ 4239191783Srmacklem struct nfsclclient *clp; 4240191783Srmacklem struct nfscldeleg *dp; 4241191783Srmacklem struct nfsnode *np; 4242191783Srmacklem struct nfsmount *nmp; 4243191783Srmacklem 4244191783Srmacklem np = VTONFS(vp); 4245191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4246191783Srmacklem if (!NFSHASNFSV4(nmp)) 4247191783Srmacklem return (1); 4248191783Srmacklem NFSLOCKCLSTATE(); 4249191783Srmacklem clp = nfscl_findcl(nmp); 4250191783Srmacklem if (clp == NULL) { 4251191783Srmacklem NFSUNLOCKCLSTATE(); 4252191783Srmacklem return (1); 4253191783Srmacklem } 4254191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4255214406Srmacklem if (dp != NULL && 4256214406Srmacklem (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 && 4257214406Srmacklem (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) == 4258214406Srmacklem NFSCLDL_WRITE)) { 4259191783Srmacklem NFSUNLOCKCLSTATE(); 4260191783Srmacklem return (0); 4261191783Srmacklem } 4262191783Srmacklem NFSUNLOCKCLSTATE(); 4263191783Srmacklem return (1); 4264191783Srmacklem} 4265191783Srmacklem 4266191783Srmacklem/* 4267191783Srmacklem * Look for an associated delegation that should be DelegReturned. 4268191783Srmacklem */ 4269191783SrmacklemAPPLESTATIC int 4270191783Srmacklemnfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp) 4271191783Srmacklem{ 4272191783Srmacklem struct nfsclclient *clp; 4273191783Srmacklem struct nfscldeleg *dp; 4274191783Srmacklem struct nfsclowner *owp; 4275191783Srmacklem struct nfscllockowner *lp; 4276191783Srmacklem struct nfsmount *nmp; 4277191783Srmacklem struct ucred *cred; 4278191783Srmacklem struct nfsnode *np; 4279191783Srmacklem int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept; 4280191783Srmacklem 4281191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4282191783Srmacklem np = VTONFS(vp); 4283191783Srmacklem NFSLOCKCLSTATE(); 4284191783Srmacklem /* 4285191783Srmacklem * Loop around waiting for: 4286191783Srmacklem * - outstanding I/O operations on delegations to complete 4287191783Srmacklem * - for a delegation on vp that has state, lock the client and 4288191783Srmacklem * do a recall 4289191783Srmacklem * - return delegation with no state 4290191783Srmacklem */ 4291191783Srmacklem while (1) { 4292191783Srmacklem clp = nfscl_findcl(nmp); 4293191783Srmacklem if (clp == NULL) { 4294191783Srmacklem NFSUNLOCKCLSTATE(); 4295191783Srmacklem return (retcnt); 4296191783Srmacklem } 4297191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 4298191783Srmacklem np->n_fhp->nfh_len); 4299191783Srmacklem if (dp != NULL) { 4300191783Srmacklem /* 4301191783Srmacklem * Wait for outstanding I/O ops to be done. 4302191783Srmacklem */ 4303191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 4304191783Srmacklem if (igotlock) { 4305191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 4306191783Srmacklem igotlock = 0; 4307191783Srmacklem } 4308191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 4309191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 4310191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 4311191783Srmacklem continue; 4312191783Srmacklem } 4313191783Srmacklem needsrecall = 0; 4314191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 4315191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 4316191783Srmacklem needsrecall = 1; 4317191783Srmacklem break; 4318191783Srmacklem } 4319191783Srmacklem } 4320191783Srmacklem if (!needsrecall) { 4321191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 4322191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 4323191783Srmacklem needsrecall = 1; 4324191783Srmacklem break; 4325191783Srmacklem } 4326191783Srmacklem } 4327191783Srmacklem } 4328191783Srmacklem if (needsrecall && !triedrecall) { 4329214406Srmacklem dp->nfsdl_flags |= NFSCLDL_DELEGRET; 4330191783Srmacklem islept = 0; 4331191783Srmacklem while (!igotlock) { 4332191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 4333222389Srmacklem &islept, NFSCLSTATEMUTEXPTR, NULL); 4334191783Srmacklem if (islept) 4335191783Srmacklem break; 4336191783Srmacklem } 4337191783Srmacklem if (islept) 4338191783Srmacklem continue; 4339191783Srmacklem NFSUNLOCKCLSTATE(); 4340191783Srmacklem cred = newnfs_getcred(); 4341191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 4342207082Srmacklem (void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0); 4343191783Srmacklem NFSFREECRED(cred); 4344191783Srmacklem triedrecall = 1; 4345191783Srmacklem NFSLOCKCLSTATE(); 4346191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 4347191783Srmacklem igotlock = 0; 4348191783Srmacklem continue; 4349191783Srmacklem } 4350191783Srmacklem *stp = dp->nfsdl_stateid; 4351191783Srmacklem retcnt = 1; 4352191783Srmacklem nfscl_cleandeleg(dp); 4353191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 4354191783Srmacklem } 4355191783Srmacklem if (igotlock) 4356191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 4357191783Srmacklem NFSUNLOCKCLSTATE(); 4358191783Srmacklem return (retcnt); 4359191783Srmacklem } 4360191783Srmacklem} 4361191783Srmacklem 4362191783Srmacklem/* 4363191783Srmacklem * Look for associated delegation(s) that should be DelegReturned. 4364191783Srmacklem */ 4365191783SrmacklemAPPLESTATIC int 4366191783Srmacklemnfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp, 4367191783Srmacklem nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p) 4368191783Srmacklem{ 4369191783Srmacklem struct nfsclclient *clp; 4370191783Srmacklem struct nfscldeleg *dp; 4371191783Srmacklem struct nfsclowner *owp; 4372191783Srmacklem struct nfscllockowner *lp; 4373191783Srmacklem struct nfsmount *nmp; 4374191783Srmacklem struct ucred *cred; 4375191783Srmacklem struct nfsnode *np; 4376191783Srmacklem int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept; 4377191783Srmacklem 4378191783Srmacklem nmp = VFSTONFS(vnode_mount(fvp)); 4379191783Srmacklem *gotfdp = 0; 4380191783Srmacklem *gottdp = 0; 4381191783Srmacklem NFSLOCKCLSTATE(); 4382191783Srmacklem /* 4383191783Srmacklem * Loop around waiting for: 4384191783Srmacklem * - outstanding I/O operations on delegations to complete 4385191783Srmacklem * - for a delegation on fvp that has state, lock the client and 4386191783Srmacklem * do a recall 4387191783Srmacklem * - return delegation(s) with no state. 4388191783Srmacklem */ 4389191783Srmacklem while (1) { 4390191783Srmacklem clp = nfscl_findcl(nmp); 4391191783Srmacklem if (clp == NULL) { 4392191783Srmacklem NFSUNLOCKCLSTATE(); 4393191783Srmacklem return (retcnt); 4394191783Srmacklem } 4395191783Srmacklem np = VTONFS(fvp); 4396191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 4397191783Srmacklem np->n_fhp->nfh_len); 4398191783Srmacklem if (dp != NULL && *gotfdp == 0) { 4399191783Srmacklem /* 4400191783Srmacklem * Wait for outstanding I/O ops to be done. 4401191783Srmacklem */ 4402191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 4403191783Srmacklem if (igotlock) { 4404191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 4405191783Srmacklem igotlock = 0; 4406191783Srmacklem } 4407191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 4408191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 4409191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 4410191783Srmacklem continue; 4411191783Srmacklem } 4412191783Srmacklem needsrecall = 0; 4413191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 4414191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 4415191783Srmacklem needsrecall = 1; 4416191783Srmacklem break; 4417191783Srmacklem } 4418191783Srmacklem } 4419191783Srmacklem if (!needsrecall) { 4420191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 4421191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 4422191783Srmacklem needsrecall = 1; 4423191783Srmacklem break; 4424191783Srmacklem } 4425191783Srmacklem } 4426191783Srmacklem } 4427191783Srmacklem if (needsrecall && !triedrecall) { 4428214406Srmacklem dp->nfsdl_flags |= NFSCLDL_DELEGRET; 4429191783Srmacklem islept = 0; 4430191783Srmacklem while (!igotlock) { 4431191783Srmacklem igotlock = nfsv4_lock(&clp->nfsc_lock, 1, 4432222389Srmacklem &islept, NFSCLSTATEMUTEXPTR, NULL); 4433191783Srmacklem if (islept) 4434191783Srmacklem break; 4435191783Srmacklem } 4436191783Srmacklem if (islept) 4437191783Srmacklem continue; 4438191783Srmacklem NFSUNLOCKCLSTATE(); 4439191783Srmacklem cred = newnfs_getcred(); 4440191783Srmacklem newnfs_copycred(&dp->nfsdl_cred, cred); 4441207082Srmacklem (void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0); 4442191783Srmacklem NFSFREECRED(cred); 4443191783Srmacklem triedrecall = 1; 4444191783Srmacklem NFSLOCKCLSTATE(); 4445191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 4446191783Srmacklem igotlock = 0; 4447191783Srmacklem continue; 4448191783Srmacklem } 4449191783Srmacklem *fstp = dp->nfsdl_stateid; 4450191783Srmacklem retcnt++; 4451191783Srmacklem *gotfdp = 1; 4452191783Srmacklem nfscl_cleandeleg(dp); 4453191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 4454191783Srmacklem } 4455191783Srmacklem if (igotlock) { 4456191783Srmacklem nfsv4_unlock(&clp->nfsc_lock, 0); 4457191783Srmacklem igotlock = 0; 4458191783Srmacklem } 4459191783Srmacklem if (tvp != NULL) { 4460191783Srmacklem np = VTONFS(tvp); 4461191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, 4462191783Srmacklem np->n_fhp->nfh_len); 4463191783Srmacklem if (dp != NULL && *gottdp == 0) { 4464191783Srmacklem /* 4465191783Srmacklem * Wait for outstanding I/O ops to be done. 4466191783Srmacklem */ 4467191783Srmacklem if (dp->nfsdl_rwlock.nfslock_usecnt > 0) { 4468191783Srmacklem dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED; 4469191783Srmacklem (void) nfsmsleep(&dp->nfsdl_rwlock, 4470191783Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL); 4471191783Srmacklem continue; 4472191783Srmacklem } 4473191783Srmacklem LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { 4474191783Srmacklem if (!LIST_EMPTY(&owp->nfsow_open)) { 4475191783Srmacklem NFSUNLOCKCLSTATE(); 4476191783Srmacklem return (retcnt); 4477191783Srmacklem } 4478191783Srmacklem } 4479191783Srmacklem LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) { 4480191783Srmacklem if (!LIST_EMPTY(&lp->nfsl_lock)) { 4481191783Srmacklem NFSUNLOCKCLSTATE(); 4482191783Srmacklem return (retcnt); 4483191783Srmacklem } 4484191783Srmacklem } 4485191783Srmacklem *tstp = dp->nfsdl_stateid; 4486191783Srmacklem retcnt++; 4487191783Srmacklem *gottdp = 1; 4488191783Srmacklem nfscl_cleandeleg(dp); 4489191783Srmacklem nfscl_freedeleg(&clp->nfsc_deleg, dp); 4490191783Srmacklem } 4491191783Srmacklem } 4492191783Srmacklem NFSUNLOCKCLSTATE(); 4493191783Srmacklem return (retcnt); 4494191783Srmacklem } 4495191783Srmacklem} 4496191783Srmacklem 4497191783Srmacklem/* 4498191783Srmacklem * Get a reference on the clientid associated with the mount point. 4499191783Srmacklem * Return 1 if success, 0 otherwise. 4500191783Srmacklem */ 4501191783SrmacklemAPPLESTATIC int 4502191783Srmacklemnfscl_getref(struct nfsmount *nmp) 4503191783Srmacklem{ 4504191783Srmacklem struct nfsclclient *clp; 4505191783Srmacklem 4506191783Srmacklem NFSLOCKCLSTATE(); 4507191783Srmacklem clp = nfscl_findcl(nmp); 4508191783Srmacklem if (clp == NULL) { 4509191783Srmacklem NFSUNLOCKCLSTATE(); 4510191783Srmacklem return (0); 4511191783Srmacklem } 4512222389Srmacklem nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL); 4513191783Srmacklem NFSUNLOCKCLSTATE(); 4514191783Srmacklem return (1); 4515191783Srmacklem} 4516191783Srmacklem 4517191783Srmacklem/* 4518191783Srmacklem * Release a reference on a clientid acquired with the above call. 4519191783Srmacklem */ 4520191783SrmacklemAPPLESTATIC void 4521191783Srmacklemnfscl_relref(struct nfsmount *nmp) 4522191783Srmacklem{ 4523191783Srmacklem struct nfsclclient *clp; 4524191783Srmacklem 4525191783Srmacklem NFSLOCKCLSTATE(); 4526191783Srmacklem clp = nfscl_findcl(nmp); 4527191783Srmacklem if (clp == NULL) { 4528191783Srmacklem NFSUNLOCKCLSTATE(); 4529191783Srmacklem return; 4530191783Srmacklem } 4531191783Srmacklem nfsv4_relref(&clp->nfsc_lock); 4532191783Srmacklem NFSUNLOCKCLSTATE(); 4533191783Srmacklem} 4534191783Srmacklem 4535191783Srmacklem/* 4536191783Srmacklem * Save the size attribute in the delegation, since the nfsnode 4537191783Srmacklem * is going away. 4538191783Srmacklem */ 4539191783SrmacklemAPPLESTATIC void 4540191783Srmacklemnfscl_reclaimnode(vnode_t vp) 4541191783Srmacklem{ 4542191783Srmacklem struct nfsclclient *clp; 4543191783Srmacklem struct nfscldeleg *dp; 4544191783Srmacklem struct nfsnode *np = VTONFS(vp); 4545191783Srmacklem struct nfsmount *nmp; 4546191783Srmacklem 4547191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4548191783Srmacklem if (!NFSHASNFSV4(nmp)) 4549191783Srmacklem return; 4550191783Srmacklem NFSLOCKCLSTATE(); 4551191783Srmacklem clp = nfscl_findcl(nmp); 4552191783Srmacklem if (clp == NULL) { 4553191783Srmacklem NFSUNLOCKCLSTATE(); 4554191783Srmacklem return; 4555191783Srmacklem } 4556191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4557191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) 4558191783Srmacklem dp->nfsdl_size = np->n_size; 4559191783Srmacklem NFSUNLOCKCLSTATE(); 4560191783Srmacklem} 4561191783Srmacklem 4562191783Srmacklem/* 4563191783Srmacklem * Get the saved size attribute in the delegation, since it is a 4564191783Srmacklem * newly allocated nfsnode. 4565191783Srmacklem */ 4566191783SrmacklemAPPLESTATIC void 4567191783Srmacklemnfscl_newnode(vnode_t vp) 4568191783Srmacklem{ 4569191783Srmacklem struct nfsclclient *clp; 4570191783Srmacklem struct nfscldeleg *dp; 4571191783Srmacklem struct nfsnode *np = VTONFS(vp); 4572191783Srmacklem struct nfsmount *nmp; 4573191783Srmacklem 4574191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4575191783Srmacklem if (!NFSHASNFSV4(nmp)) 4576191783Srmacklem return; 4577191783Srmacklem NFSLOCKCLSTATE(); 4578191783Srmacklem clp = nfscl_findcl(nmp); 4579191783Srmacklem if (clp == NULL) { 4580191783Srmacklem NFSUNLOCKCLSTATE(); 4581191783Srmacklem return; 4582191783Srmacklem } 4583191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4584191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) 4585191783Srmacklem np->n_size = dp->nfsdl_size; 4586191783Srmacklem NFSUNLOCKCLSTATE(); 4587191783Srmacklem} 4588191783Srmacklem 4589191783Srmacklem/* 4590191783Srmacklem * If there is a valid write delegation for this file, set the modtime 4591191783Srmacklem * to the local clock time. 4592191783Srmacklem */ 4593191783SrmacklemAPPLESTATIC void 4594191783Srmacklemnfscl_delegmodtime(vnode_t vp) 4595191783Srmacklem{ 4596191783Srmacklem struct nfsclclient *clp; 4597191783Srmacklem struct nfscldeleg *dp; 4598191783Srmacklem struct nfsnode *np = VTONFS(vp); 4599191783Srmacklem struct nfsmount *nmp; 4600191783Srmacklem 4601191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4602191783Srmacklem if (!NFSHASNFSV4(nmp)) 4603191783Srmacklem return; 4604191783Srmacklem NFSLOCKCLSTATE(); 4605191783Srmacklem clp = nfscl_findcl(nmp); 4606191783Srmacklem if (clp == NULL) { 4607191783Srmacklem NFSUNLOCKCLSTATE(); 4608191783Srmacklem return; 4609191783Srmacklem } 4610191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4611191783Srmacklem if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) { 4612245909Sjhb nanotime(&dp->nfsdl_modtime); 4613191783Srmacklem dp->nfsdl_flags |= NFSCLDL_MODTIMESET; 4614191783Srmacklem } 4615191783Srmacklem NFSUNLOCKCLSTATE(); 4616191783Srmacklem} 4617191783Srmacklem 4618191783Srmacklem/* 4619191783Srmacklem * If there is a valid write delegation for this file with a modtime set, 4620191783Srmacklem * put that modtime in mtime. 4621191783Srmacklem */ 4622191783SrmacklemAPPLESTATIC void 4623191783Srmacklemnfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime) 4624191783Srmacklem{ 4625191783Srmacklem struct nfsclclient *clp; 4626191783Srmacklem struct nfscldeleg *dp; 4627191783Srmacklem struct nfsnode *np = VTONFS(vp); 4628191783Srmacklem struct nfsmount *nmp; 4629191783Srmacklem 4630191783Srmacklem nmp = VFSTONFS(vnode_mount(vp)); 4631191783Srmacklem if (!NFSHASNFSV4(nmp)) 4632191783Srmacklem return; 4633191783Srmacklem NFSLOCKCLSTATE(); 4634191783Srmacklem clp = nfscl_findcl(nmp); 4635191783Srmacklem if (clp == NULL) { 4636191783Srmacklem NFSUNLOCKCLSTATE(); 4637191783Srmacklem return; 4638191783Srmacklem } 4639191783Srmacklem dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 4640191783Srmacklem if (dp != NULL && 4641191783Srmacklem (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) == 4642191783Srmacklem (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) 4643191783Srmacklem *mtime = dp->nfsdl_modtime; 4644191783Srmacklem NFSUNLOCKCLSTATE(); 4645191783Srmacklem} 4646191783Srmacklem 4647191783Srmacklemstatic int 4648191783Srmacklemnfscl_errmap(struct nfsrv_descript *nd) 4649191783Srmacklem{ 4650191783Srmacklem short *defaulterrp, *errp; 4651191783Srmacklem 4652191783Srmacklem if (!nd->nd_repstat) 4653191783Srmacklem return (0); 4654191783Srmacklem if (nd->nd_procnum == NFSPROC_NOOP) 4655191783Srmacklem return (txdr_unsigned(nd->nd_repstat & 0xffff)); 4656191783Srmacklem if (nd->nd_repstat == EBADRPC) 4657191783Srmacklem return (txdr_unsigned(NFSERR_BADXDR)); 4658191783Srmacklem if (nd->nd_repstat == NFSERR_MINORVERMISMATCH || 4659191783Srmacklem nd->nd_repstat == NFSERR_OPILLEGAL) 4660191783Srmacklem return (txdr_unsigned(nd->nd_repstat)); 4661244042Srmacklem if (nd->nd_procnum < NFSV4OP_CBNOPS) 4662244042Srmacklem errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum]; 4663244042Srmacklem else 4664244042Srmacklem return (txdr_unsigned(nd->nd_repstat)); 4665191783Srmacklem while (*++errp) 4666191783Srmacklem if (*errp == (short)nd->nd_repstat) 4667191783Srmacklem return (txdr_unsigned(nd->nd_repstat)); 4668191783Srmacklem return (txdr_unsigned(*defaulterrp)); 4669191783Srmacklem} 4670191783Srmacklem 4671244042Srmacklem/* 4672244042Srmacklem * Called to find/add a layout to a client. 4673244042Srmacklem * This function returns the layout with a refcnt (shared lock) upon 4674244042Srmacklem * success (returns 0) or with no lock/refcnt on the layout when an 4675244042Srmacklem * error is returned. 4676244042Srmacklem * If a layout is passed in via lypp, it is locked (exclusively locked). 4677244042Srmacklem */ 4678244042SrmacklemAPPLESTATIC int 4679244042Srmacklemnfscl_layout(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen, 4680244042Srmacklem nfsv4stateid_t *stateidp, int retonclose, 4681244042Srmacklem struct nfsclflayouthead *fhlp, struct nfscllayout **lypp, 4682244042Srmacklem struct ucred *cred, NFSPROC_T *p) 4683244042Srmacklem{ 4684244042Srmacklem struct nfsclclient *clp; 4685244042Srmacklem struct nfscllayout *lyp, *tlyp; 4686244042Srmacklem struct nfsclflayout *flp; 4687244042Srmacklem struct nfsnode *np = VTONFS(vp); 4688244042Srmacklem mount_t mp; 4689244042Srmacklem int layout_passed_in; 4690244042Srmacklem 4691244042Srmacklem mp = nmp->nm_mountp; 4692244042Srmacklem layout_passed_in = 1; 4693244042Srmacklem tlyp = NULL; 4694244042Srmacklem lyp = *lypp; 4695244042Srmacklem if (lyp == NULL) { 4696244042Srmacklem layout_passed_in = 0; 4697244042Srmacklem tlyp = malloc(sizeof(*tlyp) + fhlen - 1, M_NFSLAYOUT, 4698244042Srmacklem M_WAITOK | M_ZERO); 4699244042Srmacklem } 4700244042Srmacklem 4701244042Srmacklem NFSLOCKCLSTATE(); 4702244042Srmacklem clp = nmp->nm_clp; 4703244042Srmacklem if (clp == NULL) { 4704244042Srmacklem if (layout_passed_in != 0) 4705244042Srmacklem nfsv4_unlock(&lyp->nfsly_lock, 0); 4706244042Srmacklem NFSUNLOCKCLSTATE(); 4707244042Srmacklem if (tlyp != NULL) 4708244042Srmacklem free(tlyp, M_NFSLAYOUT); 4709244042Srmacklem return (EPERM); 4710244042Srmacklem } 4711244042Srmacklem if (lyp == NULL) { 4712244042Srmacklem /* 4713244042Srmacklem * Although no lyp was passed in, another thread might have 4714244042Srmacklem * allocated one. If one is found, just increment it's ref 4715244042Srmacklem * count and return it. 4716244042Srmacklem */ 4717244042Srmacklem lyp = nfscl_findlayout(clp, fhp, fhlen); 4718244042Srmacklem if (lyp == NULL) { 4719244042Srmacklem lyp = tlyp; 4720244042Srmacklem tlyp = NULL; 4721244042Srmacklem lyp->nfsly_stateid.seqid = stateidp->seqid; 4722244042Srmacklem lyp->nfsly_stateid.other[0] = stateidp->other[0]; 4723244042Srmacklem lyp->nfsly_stateid.other[1] = stateidp->other[1]; 4724244042Srmacklem lyp->nfsly_stateid.other[2] = stateidp->other[2]; 4725244042Srmacklem lyp->nfsly_lastbyte = 0; 4726244042Srmacklem LIST_INIT(&lyp->nfsly_flayread); 4727244042Srmacklem LIST_INIT(&lyp->nfsly_flayrw); 4728244042Srmacklem LIST_INIT(&lyp->nfsly_recall); 4729244042Srmacklem lyp->nfsly_filesid[0] = np->n_vattr.na_filesid[0]; 4730244042Srmacklem lyp->nfsly_filesid[1] = np->n_vattr.na_filesid[1]; 4731244042Srmacklem lyp->nfsly_clp = clp; 4732244042Srmacklem lyp->nfsly_flags = (retonclose != 0) ? 4733244042Srmacklem (NFSLY_FILES | NFSLY_RETONCLOSE) : NFSLY_FILES; 4734244042Srmacklem lyp->nfsly_fhlen = fhlen; 4735244042Srmacklem NFSBCOPY(fhp, lyp->nfsly_fh, fhlen); 4736244042Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list); 4737244042Srmacklem LIST_INSERT_HEAD(NFSCLLAYOUTHASH(clp, fhp, fhlen), lyp, 4738244042Srmacklem nfsly_hash); 4739244042Srmacklem lyp->nfsly_timestamp = NFSD_MONOSEC + 120; 4740244042Srmacklem nfscl_layoutcnt++; 4741244042Srmacklem } else { 4742244042Srmacklem if (retonclose != 0) 4743244042Srmacklem lyp->nfsly_flags |= NFSLY_RETONCLOSE; 4744244042Srmacklem TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list); 4745244042Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list); 4746244042Srmacklem lyp->nfsly_timestamp = NFSD_MONOSEC + 120; 4747244042Srmacklem } 4748244042Srmacklem nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp); 4749244042Srmacklem if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 4750244042Srmacklem NFSUNLOCKCLSTATE(); 4751244042Srmacklem if (tlyp != NULL) 4752244042Srmacklem free(tlyp, M_NFSLAYOUT); 4753244042Srmacklem return (EPERM); 4754244042Srmacklem } 4755244042Srmacklem *lypp = lyp; 4756244042Srmacklem } else 4757244042Srmacklem lyp->nfsly_stateid.seqid = stateidp->seqid; 4758244042Srmacklem 4759244042Srmacklem /* Merge the new list of File Layouts into the list. */ 4760244042Srmacklem flp = LIST_FIRST(fhlp); 4761244042Srmacklem if (flp != NULL) { 4762244042Srmacklem if (flp->nfsfl_iomode == NFSLAYOUTIOMODE_READ) 4763244042Srmacklem nfscl_mergeflayouts(&lyp->nfsly_flayread, fhlp); 4764244042Srmacklem else 4765244042Srmacklem nfscl_mergeflayouts(&lyp->nfsly_flayrw, fhlp); 4766244042Srmacklem } 4767244042Srmacklem if (layout_passed_in != 0) 4768244042Srmacklem nfsv4_unlock(&lyp->nfsly_lock, 1); 4769244042Srmacklem NFSUNLOCKCLSTATE(); 4770244042Srmacklem if (tlyp != NULL) 4771244042Srmacklem free(tlyp, M_NFSLAYOUT); 4772244042Srmacklem return (0); 4773244042Srmacklem} 4774244042Srmacklem 4775244042Srmacklem/* 4776244042Srmacklem * Search for a layout by MDS file handle. 4777244042Srmacklem * If one is found, it is returned with a refcnt (shared lock) iff 4778244042Srmacklem * retflpp returned non-NULL and locked (exclusive locked) iff retflpp is 4779244042Srmacklem * returned NULL. 4780244042Srmacklem */ 4781244042Srmacklemstruct nfscllayout * 4782244042Srmacklemnfscl_getlayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen, 4783244042Srmacklem uint64_t off, struct nfsclflayout **retflpp, int *recalledp) 4784244042Srmacklem{ 4785244042Srmacklem struct nfscllayout *lyp; 4786244042Srmacklem mount_t mp; 4787244042Srmacklem int error, igotlock; 4788244042Srmacklem 4789244042Srmacklem mp = clp->nfsc_nmp->nm_mountp; 4790244042Srmacklem *recalledp = 0; 4791244042Srmacklem *retflpp = NULL; 4792244042Srmacklem NFSLOCKCLSTATE(); 4793244042Srmacklem lyp = nfscl_findlayout(clp, fhp, fhlen); 4794244042Srmacklem if (lyp != NULL) { 4795244042Srmacklem if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) { 4796244042Srmacklem TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list); 4797244042Srmacklem TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list); 4798244042Srmacklem lyp->nfsly_timestamp = NFSD_MONOSEC + 120; 4799244042Srmacklem error = nfscl_findlayoutforio(lyp, off, 4800244042Srmacklem NFSV4OPEN_ACCESSREAD, retflpp); 4801244042Srmacklem if (error == 0) 4802244042Srmacklem nfsv4_getref(&lyp->nfsly_lock, NULL, 4803244042Srmacklem NFSCLSTATEMUTEXPTR, mp); 4804244042Srmacklem else { 4805244042Srmacklem do { 4806244042Srmacklem igotlock = nfsv4_lock(&lyp->nfsly_lock, 4807244042Srmacklem 1, NULL, NFSCLSTATEMUTEXPTR, mp); 4808244042Srmacklem } while (igotlock == 0 && 4809244042Srmacklem (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0); 4810244042Srmacklem *retflpp = NULL; 4811244042Srmacklem } 4812244042Srmacklem if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 4813244042Srmacklem lyp = NULL; 4814244042Srmacklem *recalledp = 1; 4815244042Srmacklem } 4816244042Srmacklem } else { 4817244042Srmacklem lyp = NULL; 4818244042Srmacklem *recalledp = 1; 4819244042Srmacklem } 4820244042Srmacklem } 4821244042Srmacklem NFSUNLOCKCLSTATE(); 4822244042Srmacklem return (lyp); 4823244042Srmacklem} 4824244042Srmacklem 4825244042Srmacklem/* 4826244042Srmacklem * Search for a layout by MDS file handle. If one is found that is marked 4827244042Srmacklem * "return on close", delete it, since it should now be forgotten. 4828244042Srmacklem */ 4829244042Srmacklemstatic void 4830244042Srmacklemnfscl_retoncloselayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen) 4831244042Srmacklem{ 4832244042Srmacklem struct nfscllayout *lyp; 4833244042Srmacklem 4834244042Srmacklemtryagain: 4835244042Srmacklem lyp = nfscl_findlayout(clp, fhp, fhlen); 4836244042Srmacklem if (lyp != NULL && (lyp->nfsly_flags & NFSLY_RETONCLOSE) != 0) { 4837244042Srmacklem /* 4838244042Srmacklem * Wait for outstanding I/O ops to be done. 4839244042Srmacklem */ 4840244042Srmacklem if (lyp->nfsly_lock.nfslock_usecnt != 0 || 4841244042Srmacklem lyp->nfsly_lock.nfslock_lock != 0) { 4842244042Srmacklem lyp->nfsly_lock.nfslock_lock |= NFSV4LOCK_WANTED; 4843244042Srmacklem (void)mtx_sleep(&lyp->nfsly_lock, 4844244042Srmacklem NFSCLSTATEMUTEXPTR, PZERO, "nfslyc", 0); 4845244042Srmacklem goto tryagain; 4846244042Srmacklem } 4847244042Srmacklem nfscl_freelayout(lyp); 4848244042Srmacklem } 4849244042Srmacklem} 4850244042Srmacklem 4851244042Srmacklem/* 4852244042Srmacklem * Dereference a layout. 4853244042Srmacklem */ 4854244042Srmacklemvoid 4855244042Srmacklemnfscl_rellayout(struct nfscllayout *lyp, int exclocked) 4856244042Srmacklem{ 4857244042Srmacklem 4858244042Srmacklem NFSLOCKCLSTATE(); 4859244042Srmacklem if (exclocked != 0) 4860244042Srmacklem nfsv4_unlock(&lyp->nfsly_lock, 0); 4861244042Srmacklem else 4862244042Srmacklem nfsv4_relref(&lyp->nfsly_lock); 4863244042Srmacklem NFSUNLOCKCLSTATE(); 4864244042Srmacklem} 4865244042Srmacklem 4866244042Srmacklem/* 4867244042Srmacklem * Search for a devinfo by deviceid. If one is found, return it after 4868244042Srmacklem * acquiring a reference count on it. 4869244042Srmacklem */ 4870244042Srmacklemstruct nfscldevinfo * 4871244042Srmacklemnfscl_getdevinfo(struct nfsclclient *clp, uint8_t *deviceid, 4872244042Srmacklem struct nfscldevinfo *dip) 4873244042Srmacklem{ 4874244042Srmacklem 4875244042Srmacklem NFSLOCKCLSTATE(); 4876244042Srmacklem if (dip == NULL) 4877244042Srmacklem dip = nfscl_finddevinfo(clp, deviceid); 4878244042Srmacklem if (dip != NULL) 4879244042Srmacklem dip->nfsdi_refcnt++; 4880244042Srmacklem NFSUNLOCKCLSTATE(); 4881244042Srmacklem return (dip); 4882244042Srmacklem} 4883244042Srmacklem 4884244042Srmacklem/* 4885244042Srmacklem * Dereference a devinfo structure. 4886244042Srmacklem */ 4887244042Srmacklemstatic void 4888244042Srmacklemnfscl_reldevinfo_locked(struct nfscldevinfo *dip) 4889244042Srmacklem{ 4890244042Srmacklem 4891244042Srmacklem dip->nfsdi_refcnt--; 4892244042Srmacklem if (dip->nfsdi_refcnt == 0) 4893244042Srmacklem wakeup(&dip->nfsdi_refcnt); 4894244042Srmacklem} 4895244042Srmacklem 4896244042Srmacklem/* 4897244042Srmacklem * Dereference a devinfo structure. 4898244042Srmacklem */ 4899244042Srmacklemvoid 4900244042Srmacklemnfscl_reldevinfo(struct nfscldevinfo *dip) 4901244042Srmacklem{ 4902244042Srmacklem 4903244042Srmacklem NFSLOCKCLSTATE(); 4904244042Srmacklem nfscl_reldevinfo_locked(dip); 4905244042Srmacklem NFSUNLOCKCLSTATE(); 4906244042Srmacklem} 4907244042Srmacklem 4908244042Srmacklem/* 4909244042Srmacklem * Find a layout for this file handle. Return NULL upon failure. 4910244042Srmacklem */ 4911244042Srmacklemstatic struct nfscllayout * 4912244042Srmacklemnfscl_findlayout(struct nfsclclient *clp, u_int8_t *fhp, int fhlen) 4913244042Srmacklem{ 4914244042Srmacklem struct nfscllayout *lyp; 4915244042Srmacklem 4916244042Srmacklem LIST_FOREACH(lyp, NFSCLLAYOUTHASH(clp, fhp, fhlen), nfsly_hash) 4917244042Srmacklem if (lyp->nfsly_fhlen == fhlen && 4918244042Srmacklem !NFSBCMP(lyp->nfsly_fh, fhp, fhlen)) 4919244042Srmacklem break; 4920244042Srmacklem return (lyp); 4921244042Srmacklem} 4922244042Srmacklem 4923244042Srmacklem/* 4924244042Srmacklem * Find a devinfo for this deviceid. Return NULL upon failure. 4925244042Srmacklem */ 4926244042Srmacklemstatic struct nfscldevinfo * 4927244042Srmacklemnfscl_finddevinfo(struct nfsclclient *clp, uint8_t *deviceid) 4928244042Srmacklem{ 4929244042Srmacklem struct nfscldevinfo *dip; 4930244042Srmacklem 4931244042Srmacklem LIST_FOREACH(dip, &clp->nfsc_devinfo, nfsdi_list) 4932244042Srmacklem if (NFSBCMP(dip->nfsdi_deviceid, deviceid, NFSX_V4DEVICEID) 4933244042Srmacklem == 0) 4934244042Srmacklem break; 4935244042Srmacklem return (dip); 4936244042Srmacklem} 4937244042Srmacklem 4938244042Srmacklem/* 4939244042Srmacklem * Merge the new file layout list into the main one, maintaining it in 4940244042Srmacklem * increasing offset order. 4941244042Srmacklem */ 4942244042Srmacklemstatic void 4943244042Srmacklemnfscl_mergeflayouts(struct nfsclflayouthead *fhlp, 4944244042Srmacklem struct nfsclflayouthead *newfhlp) 4945244042Srmacklem{ 4946244042Srmacklem struct nfsclflayout *flp, *nflp, *prevflp, *tflp; 4947244042Srmacklem 4948244042Srmacklem flp = LIST_FIRST(fhlp); 4949244042Srmacklem prevflp = NULL; 4950244042Srmacklem LIST_FOREACH_SAFE(nflp, newfhlp, nfsfl_list, tflp) { 4951244042Srmacklem while (flp != NULL && flp->nfsfl_off < nflp->nfsfl_off) { 4952244042Srmacklem prevflp = flp; 4953244042Srmacklem flp = LIST_NEXT(flp, nfsfl_list); 4954244042Srmacklem } 4955244042Srmacklem if (prevflp == NULL) 4956244042Srmacklem LIST_INSERT_HEAD(fhlp, nflp, nfsfl_list); 4957244042Srmacklem else 4958244042Srmacklem LIST_INSERT_AFTER(prevflp, nflp, nfsfl_list); 4959244042Srmacklem prevflp = nflp; 4960244042Srmacklem } 4961244042Srmacklem} 4962244042Srmacklem 4963244042Srmacklem/* 4964244042Srmacklem * Add this nfscldevinfo to the client, if it doesn't already exist. 4965244042Srmacklem * This function consumes the structure pointed at by dip, if not NULL. 4966244042Srmacklem */ 4967244042SrmacklemAPPLESTATIC int 4968244042Srmacklemnfscl_adddevinfo(struct nfsmount *nmp, struct nfscldevinfo *dip, 4969244042Srmacklem struct nfsclflayout *flp) 4970244042Srmacklem{ 4971244042Srmacklem struct nfsclclient *clp; 4972244042Srmacklem struct nfscldevinfo *tdip; 4973244042Srmacklem 4974244042Srmacklem NFSLOCKCLSTATE(); 4975244042Srmacklem clp = nmp->nm_clp; 4976244042Srmacklem if (clp == NULL) { 4977244042Srmacklem NFSUNLOCKCLSTATE(); 4978244042Srmacklem if (dip != NULL) 4979244042Srmacklem free(dip, M_NFSDEVINFO); 4980244042Srmacklem return (ENODEV); 4981244042Srmacklem } 4982244042Srmacklem tdip = nfscl_finddevinfo(clp, flp->nfsfl_dev); 4983244042Srmacklem if (tdip != NULL) { 4984244042Srmacklem tdip->nfsdi_layoutrefs++; 4985244042Srmacklem flp->nfsfl_devp = tdip; 4986244042Srmacklem nfscl_reldevinfo_locked(tdip); 4987244042Srmacklem NFSUNLOCKCLSTATE(); 4988244042Srmacklem if (dip != NULL) 4989244042Srmacklem free(dip, M_NFSDEVINFO); 4990244042Srmacklem return (0); 4991244042Srmacklem } 4992244042Srmacklem if (dip != NULL) { 4993244042Srmacklem LIST_INSERT_HEAD(&clp->nfsc_devinfo, dip, nfsdi_list); 4994244042Srmacklem dip->nfsdi_layoutrefs = 1; 4995244042Srmacklem flp->nfsfl_devp = dip; 4996244042Srmacklem } 4997244042Srmacklem NFSUNLOCKCLSTATE(); 4998244042Srmacklem if (dip == NULL) 4999244042Srmacklem return (ENODEV); 5000244042Srmacklem return (0); 5001244042Srmacklem} 5002244042Srmacklem 5003244042Srmacklem/* 5004244042Srmacklem * Free up a layout structure and associated file layout structure(s). 5005244042Srmacklem */ 5006244042SrmacklemAPPLESTATIC void 5007244042Srmacklemnfscl_freelayout(struct nfscllayout *layp) 5008244042Srmacklem{ 5009244042Srmacklem struct nfsclflayout *flp, *nflp; 5010244042Srmacklem struct nfsclrecalllayout *rp, *nrp; 5011244042Srmacklem 5012244042Srmacklem LIST_FOREACH_SAFE(flp, &layp->nfsly_flayread, nfsfl_list, nflp) { 5013244042Srmacklem LIST_REMOVE(flp, nfsfl_list); 5014244042Srmacklem nfscl_freeflayout(flp); 5015244042Srmacklem } 5016244042Srmacklem LIST_FOREACH_SAFE(flp, &layp->nfsly_flayrw, nfsfl_list, nflp) { 5017244042Srmacklem LIST_REMOVE(flp, nfsfl_list); 5018244042Srmacklem nfscl_freeflayout(flp); 5019244042Srmacklem } 5020244042Srmacklem LIST_FOREACH_SAFE(rp, &layp->nfsly_recall, nfsrecly_list, nrp) { 5021244042Srmacklem LIST_REMOVE(rp, nfsrecly_list); 5022244042Srmacklem free(rp, M_NFSLAYRECALL); 5023244042Srmacklem } 5024244042Srmacklem nfscl_layoutcnt--; 5025244042Srmacklem free(layp, M_NFSLAYOUT); 5026244042Srmacklem} 5027244042Srmacklem 5028244042Srmacklem/* 5029244042Srmacklem * Free up a file layout structure. 5030244042Srmacklem */ 5031244042SrmacklemAPPLESTATIC void 5032244042Srmacklemnfscl_freeflayout(struct nfsclflayout *flp) 5033244042Srmacklem{ 5034244042Srmacklem int i; 5035244042Srmacklem 5036244042Srmacklem for (i = 0; i < flp->nfsfl_fhcnt; i++) 5037244042Srmacklem free(flp->nfsfl_fh[i], M_NFSFH); 5038244042Srmacklem if (flp->nfsfl_devp != NULL) 5039244042Srmacklem flp->nfsfl_devp->nfsdi_layoutrefs--; 5040244042Srmacklem free(flp, M_NFSFLAYOUT); 5041244042Srmacklem} 5042244042Srmacklem 5043244042Srmacklem/* 5044244042Srmacklem * Free up a file layout devinfo structure. 5045244042Srmacklem */ 5046244042SrmacklemAPPLESTATIC void 5047244042Srmacklemnfscl_freedevinfo(struct nfscldevinfo *dip) 5048244042Srmacklem{ 5049244042Srmacklem 5050244042Srmacklem free(dip, M_NFSDEVINFO); 5051244042Srmacklem} 5052244042Srmacklem 5053244042Srmacklem/* 5054244042Srmacklem * Mark any layouts that match as recalled. 5055244042Srmacklem */ 5056244042Srmacklemstatic int 5057244042Srmacklemnfscl_layoutrecall(int recalltype, struct nfscllayout *lyp, uint32_t iomode, 5058244042Srmacklem uint64_t off, uint64_t len, uint32_t stateseqid, 5059244042Srmacklem struct nfsclrecalllayout *recallp) 5060244042Srmacklem{ 5061244042Srmacklem struct nfsclrecalllayout *rp, *orp; 5062244042Srmacklem 5063244042Srmacklem recallp->nfsrecly_recalltype = recalltype; 5064244042Srmacklem recallp->nfsrecly_iomode = iomode; 5065244042Srmacklem recallp->nfsrecly_stateseqid = stateseqid; 5066244042Srmacklem recallp->nfsrecly_off = off; 5067244042Srmacklem recallp->nfsrecly_len = len; 5068244042Srmacklem /* 5069244042Srmacklem * Order the list as file returns first, followed by fsid and any 5070244042Srmacklem * returns, both in increasing stateseqid order. 5071244042Srmacklem * Note that the seqids wrap around, so 1 is after 0xffffffff. 5072244042Srmacklem * (I'm not sure this is correct because I find RFC5661 confusing 5073244042Srmacklem * on this, but hopefully it will work ok.) 5074244042Srmacklem */ 5075244042Srmacklem orp = NULL; 5076244042Srmacklem LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) { 5077244042Srmacklem orp = rp; 5078244042Srmacklem if ((recalltype == NFSLAYOUTRETURN_FILE && 5079244042Srmacklem (rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE || 5080244042Srmacklem nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) || 5081244042Srmacklem (recalltype != NFSLAYOUTRETURN_FILE && 5082244042Srmacklem rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE && 5083244042Srmacklem nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) { 5084244042Srmacklem LIST_INSERT_BEFORE(rp, recallp, nfsrecly_list); 5085244042Srmacklem break; 5086244042Srmacklem } 5087244042Srmacklem } 5088244042Srmacklem if (rp == NULL) { 5089244042Srmacklem if (orp == NULL) 5090244042Srmacklem LIST_INSERT_HEAD(&lyp->nfsly_recall, recallp, 5091244042Srmacklem nfsrecly_list); 5092244042Srmacklem else 5093244042Srmacklem LIST_INSERT_AFTER(orp, recallp, nfsrecly_list); 5094244042Srmacklem } 5095244042Srmacklem lyp->nfsly_flags |= NFSLY_RECALL; 5096244042Srmacklem return (0); 5097244042Srmacklem} 5098244042Srmacklem 5099244042Srmacklem/* 5100244042Srmacklem * Compare the two seqids for ordering. The trick is that the seqids can 5101244042Srmacklem * wrap around from 0xffffffff->0, so check for the cases where one 5102244042Srmacklem * has wrapped around. 5103244042Srmacklem * Return 1 if seqid1 comes before seqid2, 0 otherwise. 5104244042Srmacklem */ 5105244042Srmacklemstatic int 5106244042Srmacklemnfscl_seq(uint32_t seqid1, uint32_t seqid2) 5107244042Srmacklem{ 5108244042Srmacklem 5109244042Srmacklem if (seqid2 > seqid1 && (seqid2 - seqid1) >= 0x7fffffff) 5110244042Srmacklem /* seqid2 has wrapped around. */ 5111244042Srmacklem return (0); 5112244042Srmacklem if (seqid1 > seqid2 && (seqid1 - seqid2) >= 0x7fffffff) 5113244042Srmacklem /* seqid1 has wrapped around. */ 5114244042Srmacklem return (1); 5115244042Srmacklem if (seqid1 <= seqid2) 5116244042Srmacklem return (1); 5117244042Srmacklem return (0); 5118244042Srmacklem} 5119244042Srmacklem 5120244042Srmacklem/* 5121244042Srmacklem * Do a layout return for each of the recalls. 5122244042Srmacklem */ 5123244042Srmacklemstatic void 5124244042Srmacklemnfscl_layoutreturn(struct nfsmount *nmp, struct nfscllayout *lyp, 5125244042Srmacklem struct ucred *cred, NFSPROC_T *p) 5126244042Srmacklem{ 5127244042Srmacklem struct nfsclrecalllayout *rp; 5128244042Srmacklem nfsv4stateid_t stateid; 5129244042Srmacklem 5130244042Srmacklem NFSBCOPY(lyp->nfsly_stateid.other, stateid.other, NFSX_STATEIDOTHER); 5131244042Srmacklem LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) { 5132244042Srmacklem stateid.seqid = rp->nfsrecly_stateseqid; 5133244042Srmacklem (void)nfsrpc_layoutreturn(nmp, lyp->nfsly_fh, 5134244042Srmacklem lyp->nfsly_fhlen, 0, NFSLAYOUT_NFSV4_1_FILES, 5135244042Srmacklem rp->nfsrecly_iomode, rp->nfsrecly_recalltype, 5136244042Srmacklem rp->nfsrecly_off, rp->nfsrecly_len, 5137244042Srmacklem &stateid, 0, NULL, cred, p, NULL); 5138244042Srmacklem } 5139244042Srmacklem} 5140244042Srmacklem 5141244042Srmacklem/* 5142244042Srmacklem * Do the layout commit for a file layout. 5143244042Srmacklem */ 5144244042Srmacklemstatic void 5145244042Srmacklemnfscl_dolayoutcommit(struct nfsmount *nmp, struct nfscllayout *lyp, 5146244042Srmacklem struct ucred *cred, NFSPROC_T *p) 5147244042Srmacklem{ 5148252074Srmacklem struct nfsclflayout *flp; 5149252074Srmacklem uint64_t len; 5150244042Srmacklem int error; 5151244042Srmacklem 5152252074Srmacklem LIST_FOREACH(flp, &lyp->nfsly_flayrw, nfsfl_list) { 5153252074Srmacklem if (flp->nfsfl_off <= lyp->nfsly_lastbyte) { 5154252074Srmacklem len = flp->nfsfl_end - flp->nfsfl_off; 5155252074Srmacklem error = nfsrpc_layoutcommit(nmp, lyp->nfsly_fh, 5156252074Srmacklem lyp->nfsly_fhlen, 0, flp->nfsfl_off, len, 5157252074Srmacklem lyp->nfsly_lastbyte, &lyp->nfsly_stateid, 5158252074Srmacklem NFSLAYOUT_NFSV4_1_FILES, 0, NULL, cred, p, NULL); 5159252100Srmacklem NFSCL_DEBUG(4, "layoutcommit err=%d\n", error); 5160252074Srmacklem if (error == NFSERR_NOTSUPP) { 5161252074Srmacklem /* If not supported, don't bother doing it. */ 5162252074Srmacklem NFSLOCKMNT(nmp); 5163252074Srmacklem nmp->nm_state |= NFSSTA_NOLAYOUTCOMMIT; 5164252074Srmacklem NFSUNLOCKMNT(nmp); 5165252074Srmacklem break; 5166252074Srmacklem } 5167252074Srmacklem } 5168244042Srmacklem } 5169244042Srmacklem} 5170244042Srmacklem 5171244042Srmacklem/* 5172244042Srmacklem * Commit all layouts for a file (vnode). 5173244042Srmacklem */ 5174244042Srmacklemint 5175244042Srmacklemnfscl_layoutcommit(vnode_t vp, NFSPROC_T *p) 5176244042Srmacklem{ 5177244042Srmacklem struct nfsclclient *clp; 5178244042Srmacklem struct nfscllayout *lyp; 5179244042Srmacklem struct nfsnode *np = VTONFS(vp); 5180244042Srmacklem mount_t mp; 5181244042Srmacklem struct nfsmount *nmp; 5182244042Srmacklem 5183244042Srmacklem mp = vnode_mount(vp); 5184244042Srmacklem nmp = VFSTONFS(mp); 5185244042Srmacklem if (NFSHASNOLAYOUTCOMMIT(nmp)) 5186244042Srmacklem return (0); 5187244042Srmacklem NFSLOCKCLSTATE(); 5188244042Srmacklem clp = nmp->nm_clp; 5189244042Srmacklem if (clp == NULL) { 5190244042Srmacklem NFSUNLOCKCLSTATE(); 5191244042Srmacklem return (EPERM); 5192244042Srmacklem } 5193244042Srmacklem lyp = nfscl_findlayout(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); 5194244042Srmacklem if (lyp == NULL) { 5195244042Srmacklem NFSUNLOCKCLSTATE(); 5196244042Srmacklem return (EPERM); 5197244042Srmacklem } 5198244042Srmacklem nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp); 5199244042Srmacklem if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 5200244042Srmacklem NFSUNLOCKCLSTATE(); 5201244042Srmacklem return (EPERM); 5202244042Srmacklem } 5203244042Srmacklemtryagain: 5204244042Srmacklem if ((lyp->nfsly_flags & NFSLY_WRITTEN) != 0) { 5205244042Srmacklem lyp->nfsly_flags &= ~NFSLY_WRITTEN; 5206244042Srmacklem NFSUNLOCKCLSTATE(); 5207244042Srmacklem NFSCL_DEBUG(4, "do layoutcommit2\n"); 5208244042Srmacklem nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, NFSPROCCRED(p), p); 5209244042Srmacklem NFSLOCKCLSTATE(); 5210244042Srmacklem goto tryagain; 5211244042Srmacklem } 5212244042Srmacklem nfsv4_relref(&lyp->nfsly_lock); 5213244042Srmacklem NFSUNLOCKCLSTATE(); 5214244042Srmacklem return (0); 5215244042Srmacklem} 5216244042Srmacklem 5217